diff --git a/.github/workflows/download.yaml b/.github/workflows/download.yaml index aa71a827..2a6979cf 100644 --- a/.github/workflows/download.yaml +++ b/.github/workflows/download.yaml @@ -2,59 +2,20 @@ name: dl_constants.php maintainer on: release: types: [published] - branches: ["app", "moodle"] + branches: ["main"] jobs: - update-frontend-files: - name: Update app version & download url - if: github.event.release.target_commitish == 'app' && !github.event.release.prerelease - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v3 - with: - ref: web - - - name: Update linux url - run: sed -i '/linux/c\const dl_linux = "${{ github.server_url }}/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/LB.Planner-x86_64.AppImage";' /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/snippets/dl_constants.php - - - name: Update linux comment - run: sed -i '/Linux download url/c\ * Linux download url [@${{ github.event.release.tag_name }}](${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.event.release.tag_name }}).' /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/snippets/dl_constants.php - - - name: Update windows url - run: sed -i '/windows/c\const dl_windows = "${{ github.server_url }}/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/LB.Planner.Setup.exe";' /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/snippets/dl_constants.php - - - name: Update windows comment - run: sed -i '/Windows download url/c\ * Windows download url [@${{ github.event.release.tag_name }}](${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.event.release.tag_name }}).' /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/snippets/dl_constants.php - - - name: Update mac url - run: sed -i '/mac/c\const dl_mac = "${{ github.server_url }}/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/LB.Planner.Setup.dmg";' /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/snippets/dl_constants.php - - - name: Update mac comment - run: sed -i '/MacOS download url/c\ * MacOS download url [@${{ github.event.release.tag_name }}](${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.event.release.tag_name }}).' /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/snippets/dl_constants.php - - - name: Commit & Push - uses: Andro999b/push@v1.3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: web - message: "Github Actions - Updated dl_constants.php to ${{ github.event.release.tag_name }}" - - name: SFTP Upload to Webserver - uses: Dylan700/sftp-upload-action@v1.1.4 - with: - server: ${{ secrets.SFTP_HOST }} - username: ${{ secrets.SFTP_USER }} - password: ${{ secrets.SFTP_PASSWORD }} - uploads: | - snippets/dl_constants.php => /htdocs/snippets/dl_constants.php update-backend-files: name: Update plugin version & download url - if: github.event.release.target_commitish == 'moodle' && !github.event.release.prerelease + if: '!github.event.release.prerelease' runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@v3 with: - ref: web + repository: 'necodeIT/lb_planner_web' + ref: main + persist-credentials: false + fetch-depth: 0 - name: Update plugin url run: sed -i '/dl_plugin/c\const dl_plugin = "${{ github.server_url }}/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/lb_planner.zip";' /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/snippets/dl_constants.php @@ -65,8 +26,9 @@ jobs: - name: Commit & Push uses: Andro999b/push@v1.3 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - branch: web + github_token: ${{ secrets.REPO_WEB_TOKEN }} + repository: 'necodeIT/lb_planner_web' + branch: main message: "Github Actions - Updated dl_constants.php to ${{ github.event.release.tag_name }}" - name: SFTP Upload to Webserver uses: Dylan700/sftp-upload-action@v1.1.4 diff --git a/.github/workflows/gen_docs.yaml b/.github/workflows/gen_docs.yaml new file mode 100644 index 00000000..a776125c --- /dev/null +++ b/.github/workflows/gen_docs.yaml @@ -0,0 +1,46 @@ +name: Generate API Docs + +on: + push: + branches: + - LP-55-Backend-Refactor # This is temporary and will be replaced by `main` once the refactor is complete + workflow_dispatch: # For manual triggering + +jobs: + generate-api-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + with: + ref: LP-55-Backend-Refactor # This is temporary and will be replaced by `main` once the refactor is complete + path: moodle + + - name: Checkout docs repo + uses: actions/checkout@v3 + with: + repository: 'necodeIT/lb_planner_docs' + ref: main + clean: false + path: docs + persist-credentials: false + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@v4.7.1 + with: + python-version: '3.12' + + - name: Generate docs + working-directory: moodle + run: python document_services.py ../docs/moodle + + - name: Push new documentation to docs repo + working-directory: docs + uses: Andro999b/push@v1.3 + with: + github_token: ${{ secrets.REPO_DOCS_TOKEN }} + repository: 'necodeIT/lb_planner_docs' + branch: main + message: "GitHub Actions - Update Web Service Documentation to ${{ github.sha }}" diff --git a/.github/workflows/gha.dist.yml b/.github/workflows/gha.dist.yml new file mode 100644 index 00000000..c7c45536 --- /dev/null +++ b/.github/workflows/gha.dist.yml @@ -0,0 +1,122 @@ +name: CI + +on: + pull_request: + types: + - opened + - synchronize + branches: + - LP-55-Backend-Refactor # Temporary. Will be changed to `main` when refactor is complete + +jobs: + Analysis: + runs-on: ubuntu-22.04 + + services: + postgres: + image: postgres:13 + env: + POSTGRES_USER: 'postgres' + POSTGRES_HOST_AUTH_METHOD: 'trust' + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 + + mariadb: + image: mariadb:10 + env: + MYSQL_USER: 'root' + MYSQL_ALLOW_EMPTY_PASSWORD: "true" + MYSQL_CHARACTER_SET_SERVER: "utf8mb4" + MYSQL_COLLATION_SERVER: "utf8mb4_unicode_ci" + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 3 + + strategy: + fail-fast: false + matrix: + php: ['7.4'] + moodle-branch: ['MOODLE_401_STABLE'] + database: [mariadb] + + steps: + - name: Check out repository code + uses: actions/checkout@v3 + with: + path: plugin + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.extensions }} + ini-values: max_input_vars=5000 + # If you are not using code coverage, keep "none". Otherwise, use "pcov" (Moodle 3.10 and up) or "xdebug". + # If you try to use code coverage with "none", it will fallback to phpdbg (which has known problems). + coverage: none + + - name: Initialise moodle-plugin-ci + run: | + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 + echo $(cd ci/bin; pwd) >> $GITHUB_PATH + echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH + sudo locale-gen en_AU.UTF-8 + echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV + + - name: Install NVM and Node # TODO: remove this step after https://github.com/moodlehq/moodle-plugin-ci/issues/309 is fixed + run: | + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm install 20 + nvm use 20 + + - name: Install moodle-plugin-ci + run: | + moodle-plugin-ci install --plugin ./plugin/lbplanner --db-host=127.0.0.1 + env: + DB: ${{ matrix.database }} + MOODLE_BRANCH: ${{ matrix.moodle-branch }} + + - name: PHP Lint + if: ${{ always() }} + run: moodle-plugin-ci phplint + + - name: PHP Copy/Paste Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ always() }} + run: moodle-plugin-ci phpcpd + + - name: PHP Mess Detector + continue-on-error: true # This step will show errors but will not fail + if: ${{ always() }} + run: moodle-plugin-ci phpmd + + - name: Moodle Code Checker + if: ${{ always() }} + run: moodle-plugin-ci phpcs --max-warnings 1 + + - name: Moodle PHPDoc Checker + if: ${{ always() }} + run: moodle-plugin-ci phpdoc --max-warnings 0 + + - name: Validating + if: ${{ always() }} + run: moodle-plugin-ci validate + + - name: Check upgrade savepoints + if: ${{ always() }} + run: moodle-plugin-ci savepoints + + - name: Mustache Lint + if: ${{ always() }} + run: moodle-plugin-ci mustache + + - name: Grunt + if: ${{ always() }} + run: moodle-plugin-ci grunt --max-lint-warnings 0 + + - name: PHPUnit tests + if: ${{ always() }} + run: moodle-plugin-ci phpunit --fail-on-warning diff --git a/.github/workflows/test_docs.yaml b/.github/workflows/test_docs.yaml new file mode 100644 index 00000000..6637377f --- /dev/null +++ b/.github/workflows/test_docs.yaml @@ -0,0 +1,28 @@ +name: API Doc Parsing + +on: + pull_request: + types: + - opened + - synchronize + branches: + - LP-55-Backend-Refactor # Temporary. Will be changed to `main` when refactor is complete + +jobs: + generate-api-docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + with: + path: moodle + + - name: Setup Python + uses: actions/setup-python@v4.7.1 + with: + python-version: '3.12' + + - name: Generate docs + working-directory: moodle + run: python document_services.py - diff --git a/.gitignore b/.gitignore index f7c27e56..b1203584 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ moodle *.zip lbplanner.dart/test/env.dart +example +.idea \ No newline at end of file diff --git a/.kateproject b/.kateproject new file mode 100644 index 00000000..b9ffc1c6 --- /dev/null +++ b/.kateproject @@ -0,0 +1,9 @@ +{ + "lspclient": { + "servers": { + "php" : { + "root": "../moodle/" + } + } + } +} \ No newline at end of file diff --git a/document_services.py b/document_services.py new file mode 100644 index 00000000..a694ac94 --- /dev/null +++ b/document_services.py @@ -0,0 +1,461 @@ +import json +import re +import sys +from os import path +import traceback as tb + +from typing import Any + +# https://regex101.com/r/kgCV7K +MOODLESTRUCT_REGEX = r"(?:['\"](\w+)['\"]\s*=>|return)\s*new\s*" \ + r"external_value\s*\(\s*(PARAM_\w+)\s*,\s*((?:(['\"]).+?\4)(?:\s*\.\s*(?:\w+::format\(\))|'.*?')*)" \ + r"(?:,\s*(\w+)(?:,\s*([^,]+?)(?:,\s*(\w+),?)?)?)?\s*\)" + +HAS_WARNED = False +CURRENT_SERVICE: str | None = None + +def warn(msg: str, *context: Any): + """Prints a warning message to the console and sets the global HAS_WARNED variable to True. + + :param str msg: The warning message to print. + """ + global HAS_WARNED + WARN = "\033[43m\033[30mWARN:\033[0m " + WARN_TAB = " \033[43m\033[33m|\033[0m " + + HAS_WARNED = True + + stack = tb.extract_stack() + stack_str = " -> ".join([f"\033[34m{frame.name}\033[0m" for frame in stack if frame != stack[-1]]) + + service_msg: str + if CURRENT_SERVICE is None: + service_msg = "outside any service" + else: + service_msg = f"in service \033[36m{CURRENT_SERVICE}\033[0m" + + print( + f"{WARN}\033[31m{msg}\033[0m {service_msg} ({stack_str})", + f"\n{WARN_TAB} " if len(context) > 0 else "", + *[f"\033[2m{c}\033[0m".replace('\n', '\n\033[0m' + WARN_TAB + " \033[2m") for c in context], + file=sys.stderr, + sep="" + ) + +def convert_php_type_to_normal_type(param_type: str) -> str: + CONVERSIONS = { + "PARAM_INT": "int", + "PARAM_TEXT": "String", + "PARAM_URL": "String", + "PARAM_BOOL": "bool", + } + + return CONVERSIONS.get(param_type, param_type) + +def explain_php_value(val: str) -> tuple[None | str | int | bool, str]: + SPECIAL_VALUES = { + "$USER->id": ("derived from token", "int"), # this exact phrase is checked for in the web UI + "null": (None, "") + } + + if val in SPECIAL_VALUES.keys(): + return SPECIAL_VALUES[val] + elif val.isnumeric(): + return (int(val), "int") + elif val.lower() in ("true", "false"): + return (val.lower() == "true", "bool") + elif val[0] + val[-1] in ("''", '""'): + + # see if the same kind of quote is within string + if val[1:-1].count(val[0]) > 0: + warn("found potentially non-literal string", val) + + return (val[1:-1], "String") + else: + warn("found unknown value", val) + return (f"unknown: {val}", "unknown") + +def parse_isrequired(inpot: str) -> bool | None: + if inpot in ('VALUE_REQUIRED', ''): + return True + elif inpot == 'VALUE_DEFAULT': + return False + else: + warn("found unparseable value for isrequired", inpot) + return None + +def parse_nullable(inpot: str) -> bool | None: + if inpot in ('', 'NULL_NOT_ALLOWED'): + return False + elif inpot == 'NULL_ALLOWED': + return True + else: + warn(f"found weird value for nullable: {inpot}") + return None + +class SlotsDict: + @property + def __dict__(self): + slots = tuple() + + for cls in self.__class__.__mro__: + if cls != SlotsDict and issubclass(cls, SlotsDict): + slots = cls.__slots__ + slots + return {name: self.__getattribute__(name) for name in slots} + +class ReturnInfo(SlotsDict): + __slots__ = ('type', 'description', 'nullable') + + def __init__(self, type: str, description: str, nullable: str): + self.type = convert_php_type_to_normal_type(type) + self.description = description + self.nullable = parse_nullable(nullable) + +class ParamInfo(SlotsDict): + __slots__ = ('type', 'description', 'required', 'default_value', 'nullable') + + def __init__(self, + type: str, + description: str, + required: str, + default_value: str, + nullable: str): + + self.type = convert_php_type_to_normal_type(type) + + defval, deftype = explain_php_value(default_value) + if defval is not None and deftype != self.type: + warn(f"Type of default value does not match parameter type - {deftype} != {self.type}", description) + + self.description = description + self.required = parse_isrequired(required) + self.default_value = defval + self.nullable = parse_nullable(nullable) + +class FunctionInfo(SlotsDict): + __slots__ = ('name', 'group', 'capabilities', 'description', 'path') + + def __init__(self, name: str, group: str, capabilities: list[str], description: str, path: str): + self.name = name + self.group = group + self.capabilities = capabilities + self.description = description + self.path = path + +class FunctionInfoEx(FunctionInfo): + __slots__ = ('parameters', 'returns', 'returns_multiple') + + def __init__(self, + parent: FunctionInfo, + parameters: dict[str, ParamInfo], + returns: dict[str, ReturnInfo], + returns_multiple: bool): + super().__init__(**parent.__dict__) + + self.parameters = parameters + self.returns = returns + self.returns_multiple = returns_multiple + +def extract_function_info(file_content: str) -> list[FunctionInfo]: + function_info = [] + + # Removing comments, PHP tags, and definitions + clean_content = re.sub(r"//.*|<\?php|defined\(.*\)\s*\|\|\s*die\(\);", "", file_content) + + # Splitting the content based on function definition blocks + # https://regex101.com/r/qyzYks + functions = re.findall(r"'(local_lbplanner_(\w+?)_(\w+))' => \[(.*?)\],", clean_content, re.DOTALL) + + for function in functions: + func_dict = {} + + # Extracting function name and group + func_dict["name"] = function[2] + func_dict["group"] = function[1] + + # Extracting and adjusting capabilities + capabilities = re.search(r"'capabilities' => '(.*?:.*?)'", function[3]) + if capabilities is None: + # check if call needs no capabilities + capabilities = re.search(r"'capabilities' => ''", function[3]) + func_dict["capabilities"] = [] if capabilities else None + else: + func_dict["capabilities"] = [cap.strip() for cap in capabilities.group(1).split(',') if len(cap) > 0] + + # Extracting description + description = re.search(r"'description' => '(.*?)'", function[3]) + func_dict["description"] = description.group(1) if description else None + + # Extracting and adjusting path + classpath = re.search(r"'classpath' => 'local/(.*?)'", function[3]) + func_dict["path"] = classpath.group(1) if classpath else None + + # Only adding to the list if all information is present + if all(value is not None for value in func_dict.values()): + function_info.append(FunctionInfo(**func_dict)) + else: + warn(f"Could not gather all info for {func_dict["name"]}", func_dict) + + if len(function_info) == 0: + warn("Couldn't find any functions!") + + return function_info + + +def extract_php_functions(php_code: str, name: str) -> tuple[str | None, str | None]: + # Regular expression to match the function names and bodies + # https://regex101.com/r/9GtIMA + pattern = re.compile(r"(public static function (\w+_(?:returns|parameters))\W[^{}]*?{[^{}]+?})", re.DOTALL) + + # Find all matches in the PHP code + matches: list[tuple[str, str]] = pattern.findall(php_code) + + parameters_function = None + returns_function = None + + for match in matches: + # Extract function name + function_name = match[1] + + if function_name.endswith("_parameters"): + parameters_function = match[0] + elif function_name.endswith("_returns"): + returns_function = match[0] + + if parameters_function is None: + warn(f"Couldn't find parameters function in {name}") + if returns_function is None: + warn(f"Couldn't find returns function in {name}") + + return parameters_function, returns_function + +def parse_imports(input_str: str, symbol: str) -> str | None: + use_pattern = fr"use ((?:\w+\\)+){symbol};" + uses: list[str] = re.findall(use_pattern, input_str) + + namespaces = { # it's technically possible to import from outside /classes/ + "local_lbplanner\\helpers": "classes/helpers", + "local_lbplanner\\enums": "classes/enums", + "local_lbplanner\\polyfill": "classes/polyfill", + "local_lbplanner\\model": "classes/model", + } + fp_l: list[str] = [] + for use in uses: + for namespace, p in namespaces.items(): + if use.startswith(namespace): + fp_l.append(path.join(path.dirname(__file__), "lbplanner", p, f"{symbol}.php")) + + if len(fp_l) > 1: + warn(f"found potential import collision for {symbol}", input_str) + return None + elif len(fp_l) == 0: + warn(f"Couldn't find symbol: {symbol}", input_str) + return None + else: + return fp_l[0] + +def parse_phpstuff(inpot: str) -> str: + # https://regex101.com/r/p5FzCh + casepattern = r"const (\w+) = (\d+|true|false|(['\"]).*?\3)" + + if inpot.endswith('::format()'): + enum_name = inpot[:-10] + fullbody_pattern = f"class {enum_name} extends Enum {{.*?}}" + + fp = f"lbplanner/classes/enums/{enum_name}.php" + if not path.exists(fp): + warn(f"Couldn't find enum file {fp}") + return "" + with open(fp, "r") as f: + matches: list[str] = re.findall(fullbody_pattern, f.read(), re.DOTALL) + if len(matches) == 1: + body = matches[0] + else: + warn(f"couldn't parse enum {enum_name}", matches) + + cases = {} + matches = re.findall(casepattern, body) + for match in matches: + # capitalizing first letter, if exists + name = "".join([match[0][0].upper(), match[0][1:].lower()]) + cases[name] = match[1].replace("'", '"') + + return "{ " + ", ".join([f"{name} = {value}" for name, value in cases.items()]) + " }" + else: + warn('unknown phpstuff', inpot) + return "" + +def parse_phpstring(inpot: str) -> str: + WHITESPACE = '. \t\n\r' # the . is for string concat in php + + out = [] + strlit = False + quotetype = '' + tmp_refarr: list[str] = [] + for char in inpot: + if char in '\'"': + if not strlit: + strlit = True + quotetype = char + if len(tmp_refarr) > 0: + out.append(parse_phpstuff("".join(tmp_refarr))) + tmp_refarr = [] + else: + if char == quotetype: + strlit = False + else: + out.append(char) + else: + if strlit: + out.append(char) + else: + if char in WHITESPACE: + continue + else: + tmp_refarr.append(char) + + if len(tmp_refarr) > 0: + out.append(parse_phpstuff("".join(tmp_refarr))) + tmp_refarr = [] + + return "".join(out) + +def parse_returns(input_str: str, file_content: str, name: str) -> tuple[dict[str, ReturnInfo], bool]: + # https://regex101.com/r/gUtsX3/ + redir_pattern = r"(\w+)::(\w+)(? 1: + warn(f"Couldn't parse return values in {name}", input_str) + return ({}, False) + + if len(redir_matches) == 1: + match = redir_matches[0] + meth_pattern = rf"public static function {match[1]}\(\)(?: ?: ?\w+)? ?{{(?P.*?)}}" + + fp = parse_imports(file_content, match[0]) + if fp is None: + # already warned in parse_imports, we don't need to warn again + return {}, is_multiple_structure + + with open(fp, "r") as f: + new_file_content = f.read() + meth_matches: list[str] = re.findall(meth_pattern, new_file_content, re.DOTALL) + if len(meth_matches) == 0: + warn(f"Couldn't find {match[0]}::{match[1]}() inside {fp} for {name}") + return ({}, False) + elif len(meth_matches) > 1: + raise Exception(f"Found multiple definitions for {match[0]}::{match[1]}() inside {fp}") + else: + result = parse_returns(meth_matches[0], new_file_content, fp) + + # if multiple_structure is detected here, add it + if is_multiple_structure: + return (result[0], True) + else: + return result + + matches: list[list[str]] = re.findall(MOODLESTRUCT_REGEX, input_str) + + output_dict = {} + for match in matches: + key = match[0] + if key is None: + if len(matches) > 1: + warn("got empty return key name in a structure larger than 1", matches) + else: + key = '' + + if not parse_isrequired(match[4]): + warn(f"found optional value in returns structure for {name}", input_str) + + default_str = match[5] + if default_str not in ('null', ''): + warn(f"found non-null 'default value' in returns structure for {name}: {default_str}", input_str) + + output_dict[key] = ReturnInfo(match[1], parse_phpstring(match[2]), match[6]) + + if len(output_dict) == 0: + if re.match(nullensure_pattern, input_str) is None: + warn(f"could not find any returns in non-empty {name}", input_str) + + return output_dict, is_multiple_structure + +def parse_params(input_text: str) -> dict[str, ParamInfo]: + # Regular expression to match the parameters inside the 'new external_value()' function + + # Find all matches of the pattern in the input text + matches: list[list[str]] = re.findall(MOODLESTRUCT_REGEX, input_text) + + if len(matches) == 0: + nullensure_pattern = r".*return new external_function_parameters(\s*\[\]\s*);.*" + if re.match(nullensure_pattern, input_text) is not None: + warn("could not parse params", input_text) + return {} + + result = {} + for match in matches: + param_name = match[0] + result[param_name] = ParamInfo( + match[1], + parse_phpstring(match[2]), + match[4], + match[5], + match[6], + ) + + return result + + +if __name__ == "__main__": + with open("lbplanner/db/services.php", "r") as file: + content = file.read() + infos = extract_function_info(content) + + complete_info = [] + + for i, info in enumerate(infos): + + CURRENT_SERVICE = info.name + + with open(info.path, "r") as func_file: + func_content = func_file.read() + params_func, returns_func = extract_php_functions(func_content, info.path) + + if returns_func is None or params_func is None: + continue + + returns, returns_multiple = parse_returns(returns_func, func_content, info.path) + + params = parse_params(params_func) + + complete_info.append(FunctionInfoEx(info, params, returns, returns_multiple)) + + CURRENT_SERVICE = None + + data = json.dumps(complete_info, default=lambda x: x.__dict__) + + if sys.argv[1] == "-": + print(data) + else: + declaration = f"const funcs = {data}" + + script: str + with open(f"{sys.argv[1]}/script.js", "r") as f: + script = f.read() + lines = script.splitlines() + for i in range(len(lines)): + if lines[i].startswith('const funcs = '): + lines[i] = declaration + script = "\n".join(lines) + + with open(f"{sys.argv[1]}/script.js", "w") as f: + f.write(script) + + if HAS_WARNED: + sys.exit(1) diff --git a/lbplanner/classes/enums/CAPABILITY.php b/lbplanner/classes/enums/CAPABILITY.php new file mode 100644 index 00000000..22e44a83 --- /dev/null +++ b/lbplanner/classes/enums/CAPABILITY.php @@ -0,0 +1,67 @@ +. + +/** + * capability string + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; +use local_lbplanner\enums\CAPABILITY_FLAG; + +/** + * Capabilities a user can have + */ +class CAPABILITY extends Enum { + /** + * Shortname of the admin CAPABILITY. + */ + const ADMIN = 'local/lb_planner:admin'; + + /** + * Shortname of the manager CAPABILITY. + */ + const MANAGER = 'local/lb_planner:manager'; + + /** + * Shortname of the teacher CAPABILITY. + */ + const TEACHER = 'local/lb_planner:teacher'; + + /** + * Shortname of the student CAPABILITY. + */ + const STUDENT = 'local/lb_planner:student'; + + /** + * Matches a capability string to its bitmappable flag + * @param string $str the capability string + * @return int the bitmappable flag + * @link CAPABILITY_FLAG + */ + public static function to_capability(string $str): int { + $name = self::name_from($str); + return constant('CAPABILITY_FLAG::'.$name); + } +} diff --git a/lbplanner/classes/enums/CAPABILITY_FLAG.php b/lbplanner/classes/enums/CAPABILITY_FLAG.php new file mode 100644 index 00000000..5419ebf5 --- /dev/null +++ b/lbplanner/classes/enums/CAPABILITY_FLAG.php @@ -0,0 +1,67 @@ +. + +/** + * capability string + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; +use local_lbplanner\enums\CAPABILITY; + +/** + * Bitmappable flags for capabilities a user can have + */ +class CAPABILITY_FLAG extends Enum { + /** + * Flag of the admin CAPABILITY. + */ + const ADMIN = 1; + + /** + * Flag of the manager CAPABILITY. + */ + const MANAGER = 2; + + /** + * Flag of the teacher CAPABILITY. + */ + const TEACHER = 4; + + /** + * Flag of the student CAPABILITY. + */ + const STUDENT = 8; + + /** + * matches a flag to its capability string + * @param int $num the bitmappable flag + * @return string the capability string + * @link CAPABILITY + */ + public static function to_capability(int $num): string { + $name = self::name_from($num); + return constant('CAPABILITY::'.$name); + } +} diff --git a/lbplanner/classes/enums/MODULE_GRADE.php b/lbplanner/classes/enums/MODULE_GRADE.php new file mode 100644 index 00000000..20714e9a --- /dev/null +++ b/lbplanner/classes/enums/MODULE_GRADE.php @@ -0,0 +1,56 @@ +. + +/** + * enum for module grade + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * Grades a module can receive + */ +class MODULE_GRADE extends Enum { + /** + * Erweiterte Kompetenz vollständig. + */ + const EKV = 0; + /** + * Erweiterte Kompetenz überwiegend. + */ + const EK = 1; + /** + * Grundlegende Kompetenz vollständig. + */ + const GKV = 2; + /** + * Grundlegende Kompetenz überwiegend. + */ + const GK = 3; + /** + * Negative grade. + */ + const RIP = 4; +} diff --git a/lbplanner/classes/enums/MODULE_STATUS.php b/lbplanner/classes/enums/MODULE_STATUS.php new file mode 100644 index 00000000..6d78ea59 --- /dev/null +++ b/lbplanner/classes/enums/MODULE_STATUS.php @@ -0,0 +1,52 @@ +. + +/** + * enum for module status + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * Stati a module can be in + */ +class MODULE_STATUS extends Enum { + /** + * Finished module. + */ + const DONE = 0; + /** + * Uploaded module. + */ + const UPLOADED = 1; + /** + * Overdue module. + */ + const LATE = 2; + /** + * Todo module. + */ + const PENDING = 3; +} diff --git a/lbplanner/classes/enums/MODULE_TYPE.php b/lbplanner/classes/enums/MODULE_TYPE.php new file mode 100644 index 00000000..6eb68d2f --- /dev/null +++ b/lbplanner/classes/enums/MODULE_TYPE.php @@ -0,0 +1,56 @@ +. + +/** + * enum for module type + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * Module Types + */ +class MODULE_TYPE extends Enum { + /** + * Grundlegende Kompetenz. + */ + const GK = 0; + /** + * Erweiterte Kompetenz. + */ + const EK = 1; + /** + * Test i.e. exam. + */ + const TEST = 2; + /** + * TODO: ??? + */ + const M = 3; + /** + * TODO: ??? + */ + const NONE = 4; +} diff --git a/lbplanner/classes/enums/NOTIF_STATUS.php b/lbplanner/classes/enums/NOTIF_STATUS.php new file mode 100644 index 00000000..25818738 --- /dev/null +++ b/lbplanner/classes/enums/NOTIF_STATUS.php @@ -0,0 +1,44 @@ +. + +/** + * enum for notif status + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * Stati a notification can be in + */ +class NOTIF_STATUS extends Enum { + /** + * unread notification + */ + const UNREAD = 0; + /** + * read notification + */ + const READ = 1; +} diff --git a/lbplanner/classes/enums/NOTIF_TRIGGER.php b/lbplanner/classes/enums/NOTIF_TRIGGER.php new file mode 100644 index 00000000..cfdf2c17 --- /dev/null +++ b/lbplanner/classes/enums/NOTIF_TRIGGER.php @@ -0,0 +1,68 @@ +. + +/** + * enum for notif trigger + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * Possible triggers for sending a notification + */ +class NOTIF_TRIGGER extends Enum { + /** + * Invitation sent + */ + const INVITE = 0; + /** + * Invitation accepted + */ + const INVITE_ACCEPTED = 1; + /** + * Invitation declined + */ + const INVITE_DECLINED = 2; + /** + * User left the plan + */ + const PLAN_LEFT = 3; + /** + * User got removed from the plan + */ + const PLAN_REMOVED = 4; + /** + * User registered + */ + const USER_REGISTERED = 5; + /** + * Unbook requested by supervisor + */ + const UNBOOK_REQUESTED = 6; + /** + * Unbook forced by supervisor + */ + const UNBOOK_FORCED = 7; +} diff --git a/lbplanner/classes/enums/PLAN_ACCESS_TYPE.php b/lbplanner/classes/enums/PLAN_ACCESS_TYPE.php new file mode 100644 index 00000000..71ad83cb --- /dev/null +++ b/lbplanner/classes/enums/PLAN_ACCESS_TYPE.php @@ -0,0 +1,52 @@ +. + +/** + * enum for plan access type + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * Levels of access that a user can have for a plan + */ +class PLAN_ACCESS_TYPE extends Enum { + /** + * owning the plan + */ + const OWNER = 0; + /** + * allowed to modify the plan + */ + const WRITE = 1; + /** + * allowed to look at the plan + */ + const READ = 2; + /** + * disallowed + */ + const NONE = -1; +} diff --git a/lbplanner/classes/enums/PLAN_EK.php b/lbplanner/classes/enums/PLAN_EK.php new file mode 100644 index 00000000..a14b7de2 --- /dev/null +++ b/lbplanner/classes/enums/PLAN_EK.php @@ -0,0 +1,44 @@ +. + +/** + * enum for plan EK + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * Whether EK modules are enabled inside the planner + */ +class PLAN_EK extends Enum { + /** + * EK hidden + */ + const DISABLED = 0; + /** + * EK visible + */ + const ENABLED = 1; +} diff --git a/lbplanner/classes/enums/PLAN_INVITE_STATE.php b/lbplanner/classes/enums/PLAN_INVITE_STATE.php new file mode 100644 index 00000000..f039479d --- /dev/null +++ b/lbplanner/classes/enums/PLAN_INVITE_STATE.php @@ -0,0 +1,52 @@ +. + +/** + * enum for plan invite state + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * States an invite can be in + */ +class PLAN_INVITE_STATE extends Enum { + /** + * pending invite + */ + const PENDING = 0; + /** + * accepted invite + */ + const ACCEPTED = 1; + /** + * declined invite + */ + const DECLINED = 2; + /** + * expired invite + */ + const EXPIRED = 3; +} diff --git a/lbplanner/classes/enums/WEEKDAY.php b/lbplanner/classes/enums/WEEKDAY.php new file mode 100644 index 00000000..407c409d --- /dev/null +++ b/lbplanner/classes/enums/WEEKDAY.php @@ -0,0 +1,67 @@ +. + +/** + * enum for weekdays + * (cringe, ik, but we need these defined concretely) + * + * @package local_lbplanner + * @subpackage enums + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\enums; + +// TODO: revert to native enums once we migrate to php8. + +use local_lbplanner\polyfill\Enum; + +/** + * All the days of the week. + * All seven of them. + * Yup. + */ +class WEEKDAY extends Enum { + /** + * monday + */ + const MONDAY = 1; + /** + * tuesday + */ + const TUESDAY = 2; + /** + * wednesday + */ + const WEDNESDAY = 3; + /** + * thursday + */ + const THURSDAY = 4; + /** + * friday + */ + const FRIDAY = 5; + /** + * saturday + */ + const SATURDAY = 6; + /** + * sunday + */ + const SUNDAY = 7; +} diff --git a/lbplanner/classes/helpers/config_helper.php b/lbplanner/classes/helpers/config_helper.php index e64fd382..57c65be4 100644 --- a/lbplanner/classes/helpers/config_helper.php +++ b/lbplanner/classes/helpers/config_helper.php @@ -16,27 +16,100 @@ namespace local_lbplanner\helpers; +use core_component; +use core_customfield\category_controller; +use customfield_select\field_controller; +use local_modcustomfields\customfield\mod_handler; + +/** + * Helper class for config + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class config_helper { + /** + * Sets the current active year + */ public static function set_default_active_year() { $currentmonth = idate('m'); + $currentyear = idate('Y') % 100; + $lastyear = $currentyear - 1; + $nextyear = $currentyear + 1; + // Adding the default active year, when the plugin is installed for the first time. + // If the current month is between August and December, the default active year is set to current year and the next year. if ($currentmonth >= 8 && $currentmonth <= 12) { - // Adding the default active year, when the plugin is installed for the first time. set_config( 'defaultactiveyear', - substr(strval(idate('Y')), 2) - .'/'. - substr(strval(idate('Y') + 1), 2), + $currentyear . '/' . $nextyear, 'local_lbplanner' ); + // If the current month is between January and July, the default active year is set to the previous year and the + // current year. } else { - // Adding the default active year, when the plugin is installed for the first time. set_config( 'defaultactiveyear', - substr(strval(idate('Y') - 1), 2) - .'/'. - substr(strval(idate('Y')), 2), + $lastyear . '/' . $currentyear, 'local_lbplanner' ); } } + + /** + * Adds a customfield to moodle for each activity where teachers can select GK EK or both. + * + * Default value is GK. + * @throws \coding_exception + * @throws \moodle_exception + * @throws \coding_exception + */ + public static function add_customfield(): void { + // Check if the category is already created and only create it if it doesn't exist. + // Check if plugin "modcustomfields" is installed and create the category and the custom field. + if (!get_config('local_lbplanner', 'categoryid')) { + + if (array_key_exists('modcustomfields', core_component::get_plugin_list('local'))) { + + $handler = mod_handler::create(); + $categoryid = $handler->create_category('LB Planner'); + + set_config('categoryid', $categoryid, 'local_lbplanner'); + $categorycontroller = category_controller::create($categoryid, null, $handler); + $categorycontroller->save(); + + // Dont ask me why but moodle doesnt allow me to just insert the String "select" into the type field. + $record = new \stdClass(); + $record->type = 'select'; + + $fieldcontroller = field_controller::create(0, $record, $categorycontroller); + // Added the default attributes for the custom field. + $fieldcontroller->set('name', 'LB Planner Task Type'); + $fieldcontroller->set('description', 'Tracks whether the task is GK/EK/GKandEK/TEST/SA/M'); + $fieldcontroller->set('type', 'select'); + // Because moodle wants me to save the configdata as a json string, I have to do this. + // I don't know why moodle does this, but it does. I don't like it. but I have to do it. so I do it. + $fieldcontroller->set( + 'configdata', + '{"required":"1","uniquevalues":"0","options":"GK\r\nEK\r\nGK and EK\r\nTEST\r\nSA\r\nM", + "defaultvalue":"GK","locked":"0","visibility":"2"}' + ); + $fieldcontroller->set('shortname', 'lb_planner_gk_ek'); + $fieldcontroller->save(); + } + } + } + + /** + * Get the category id from the config + * @return int the category id if it is set, -1 otherwise + */ + public static function get_category_id(): int { + if (!get_config('local_lbplanner', 'categoryid')) { + return -1; + } else { + return intval(get_config('local_lbplanner', 'categoryid')); + } + } } diff --git a/lbplanner/classes/helpers/course_helper.php b/lbplanner/classes/helpers/course_helper.php index 78927bcf..dfce13c3 100644 --- a/lbplanner/classes/helpers/course_helper.php +++ b/lbplanner/classes/helpers/course_helper.php @@ -16,160 +16,157 @@ namespace local_lbplanner\helpers; -use stdClass; - +use context_course; +use dml_exception; + +use local_lbplanner\model\course; + +/** + * Helper class for courses + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class course_helper { - - - /** - * Name of the Enrol Table - */ - const ENROL_TABLE = 'enrol'; - - /** - * Name of the User Enroll Table - */ - const USER_ENROL_TABLE = 'user_enrolments'; - - /** - * Name of the Course Table - */ - const COURSE_TABLE = 'course'; - /** - * Name of the Category Table + * The course table used by the LP */ - const CATEGORY_TABLE = 'course_categories'; - const LBPLANNER_COURSE_TABLE = 'local_lbplanner_courses'; - const COLORS = array( - "#f50057", - "#536dfe", - "#f9a826", - "#00bfa6", - "#9b59b6", - "#37bbca", - "#e67e22", - "#37CA48", - "#CA3737", - "#B5CA37", - "#37CA9E", - "#3792CA", - "#376ECA", - "#8B37CA", - "#CA37B9", - ); - - const DISABLED_COURSE = 0; - const ENABLED_COURSE = 1; - /** - * Get all the courses of the user - * @param int userid The id of the user - * - * @return array of EnrollIds + * A list of nice colors to choose from :) */ - public static function get_enrollments(int $userid) { - global $DB; - $enrollments = array(); - $records = $DB->get_records(self::USER_ENROL_TABLE, array ('userid' => $userid)); - foreach ($records as $record) { - if (in_array($record->enrolid, $enrollments) === false) { - $enrollments[] = $record->enrolid; - } - } - return $enrollments; - } + const COLORS + = [ + "#f50057", + "#536dfe", + "#f9a826", + "#00bfa6", + "#9b59b6", + "#37bbca", + "#e67e22", + "#37CA48", + "#CA3737", + "#B5CA37", + "#37CA9E", + "#3792CA", + "#376ECA", + "#8B37CA", + "#CA37B9", + ]; /** - * Get the current year + * Get the current school year from the config + * Definition of a school year: 2020/2021 + * Check in config_helper.php for more info how the date is set for defaultactiveyear * - * @return string the current year the last 2 digits (20(20)) + * @return string the current year the last 2 digits (20/20) + * @throws dml_exception */ - public static function get_current_year() : string { - if (strpos(get_config('local_lbplanner', 'activeyear'), '/' ) !== false) { + public static function get_current_year(): string { + if (strpos(get_config('local_lbplanner', 'activeyear'), '/') !== false) { return get_config('local_lbplanner', 'activeyear'); } return get_config('local_lbplanner', 'defaultactiveyear'); } /** - * Get the current category id + * Get course from lbpanner DB * - * @return int id of the current category - */ - public static function get_current_category() : int { - global $DB; - - return $DB->get_record_sql( - 'SELECT id FROM ' . self::CATEGORY_TABLE . ' WHERE name LIKE "%' . self::get_current_year() . '%"' - ); - } - /** - * Get course from mdl DB + * @param int $courseid id of the course in lbplanner + * @param int $userid id of the user * - * @param int $courseid id of the course - * @return stdClass course from moodle + * @return course course from lbplanner + * @throws dml_exception */ - public static function get_mdl_course($courseid) : stdClass { + public static function get_lbplanner_course(int $courseid, int $userid): course { global $DB; - return $DB->get_record(self::COURSE_TABLE, array('id' => $courseid)); + return course::from_db($DB->get_record(self::LBPLANNER_COURSE_TABLE, ['courseid' => $courseid, 'userid' => $userid])); } + /** - * Get course from lbpanner DB - * - * @param int $courseid id of the course in lbplanner - * @return stdClass course from lbplanner + * Get all the courses of the current year. + * @return course[] all courses of the current year */ - public static function get_lbplanner_course($courseid, $userid) : stdClass { - global $DB; - return $DB->get_record(self::LBPLANNER_COURSE_TABLE, array('courseid' => $courseid, 'userid' => $userid)); + public static function get_all_lbplanner_courses(): array { + global $DB, $USER; + $userid = $USER->id; + + $mdlcourses = enrol_get_my_courses(); + // Remove Duplicates. + $mdlcourses = array_unique($mdlcourses, SORT_REGULAR); + // Check this out: https://www.youtube.com/watch?v=WmdAk2zyQkU . + $results = []; + + foreach ($mdlcourses as $mdlcourse) { + $courseid = $mdlcourse->id; + // Check if the course is from the current year. + // TODO: pass fullname to function instead of courseid. + if (!self::check_current_year($courseid)) { + continue; + } + // Check if the course is already in the LB Planner database. + if ($DB->record_exists(self::LBPLANNER_COURSE_TABLE, ['courseid' => $courseid, 'userid' => $userid])) { + $fetchedcourse = self::get_lbplanner_course($courseid, $userid); + } else { + // IF not create an Object to be put into the LB Planner database. + $fetchedcourse = new course( + 0, $courseid, $userid, + course::prepare_shortname($mdlcourse->shortname), + self::COLORS[array_rand(self::COLORS)], + false, + ); + $fetchedcourse->set_fresh( + $DB->insert_record( + self::LBPLANNER_COURSE_TABLE, + $fetchedcourse->prepare_for_db() + ) + ); + } + // Add name to fetched Course. + $fetchedcourse->set_fullname($mdlcourse->fullname); + array_push($results, $fetchedcourse); + } + return $results; } /** * Check if the user is enrolled in the course * * @param int $courseid course id - * @param int $userid user id + * @param int $userid user id + * * @return bool true if the user is enrolled */ - public static function check_access($courseid, $userid) : bool { - global $DB; - $enrolmentids = $DB->get_records(self::ENROL_TABLE, array('courseid' => $courseid), '', '*'); - foreach ($enrolmentids as $enrolmentid) { - if ($DB->record_exists(self::USER_ENROL_TABLE, array('enrolid' => $enrolmentid->id, 'userid' => $userid))) { - return true; - } - } - return false; + public static function check_access(int $courseid, int $userid): bool { + $context = context_course::instance($courseid); + return is_enrolled($context, $userid, '', true); } + /** * gets the fullname from a course * * @param int $courseid the course id + * * @return string the fullname of the course + * @throws dml_exception */ - public static function get_fullname($courseid) { - global $DB; - return $DB->get_record(self::COURSE_TABLE, array('id' => $courseid), '*', MUST_EXIST)->fullname; + public static function get_fullname(int $courseid): string { + return get_course($courseid)->fullname; } + /** * Check if the course is from the current year * * @param int $courseid the course id + * * @return bool true if the course is from the current year + * @throws dml_exception */ - public static function check_current_year($courseid) { - if (strpos(self::get_fullname($courseid), self::get_current_year()) !== false) { - return true; - } else { - return false; - } - } - public static function get_courseid($enrolmentid) { - global $DB; - return $DB->get_record(self::ENROL_TABLE, array('id' => $enrolmentid), '*', MUST_EXIST)->courseid; + public static function check_current_year(int $courseid): bool { + return strpos(self::get_fullname($courseid), self::get_current_year()) !== false; } } diff --git a/lbplanner/classes/helpers/feedback_helper.php b/lbplanner/classes/helpers/feedback_helper.php index ceaa739c..a6931134 100644 --- a/lbplanner/classes/helpers/feedback_helper.php +++ b/lbplanner/classes/helpers/feedback_helper.php @@ -19,16 +19,35 @@ use external_function_parameters; use external_single_structure; use external_value; +use local_lbplanner\enums\CAPABILITY; use stdClass; use local_lbplanner\helpers\user_helper; +// TODO: use enums. + +/** + * Helper class for feedback + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class feedback_helper { + /** + * Unread Feedback + */ const STATUS_UNREAD = 0; + /** + * Read Feedback + */ const STATUS_READ = 1; - + /** + * The name of the table which is used by LP to store feedback in + */ const LBPLANNER_FEEDBACK_TABLE = 'local_lbplanner_feedback'; /** @@ -36,9 +55,9 @@ class feedback_helper { * * @return external_single_structure The structure of a module. */ - public static function structure() : external_single_structure { + public static function structure(): external_single_structure { return new external_single_structure( - array( + [ 'content' => new external_value(PARAM_TEXT, 'Content of the feedback'), 'userid' => new external_value(PARAM_INT, 'The id of the user'), 'type' => new external_value(PARAM_INT, 'The Type of the feedback'), @@ -49,33 +68,41 @@ public static function structure() : external_single_structure { 'lastmodified' => new external_value(PARAM_INT, 'The time when the feedback was last modified'), 'lastmodifiedby' => new external_value(PARAM_INT, 'The id of the user who last modified the feedback'), 'logfile' => new external_value(PARAM_TEXT, 'The logs of the feedback'), - ) + ] ); } + /** * Gives back the feedback of the given feedbackid * - * @param integer $feedbackid The id of the feedback + * @param int $feedbackid The id of the feedback + * * @return stdClass The feedback */ - public static function get_feedback(int $feedbackid) : stdClass { + public static function get_feedback(int $feedbackid): stdClass { global $DB; return $DB->get_record(self::LBPLANNER_FEEDBACK_TABLE, ['id' => $feedbackid]); } + /** * Checks if the user has access to feedback * - * @param integer $userid The id of the user - * @return void Throws an exception if the user has no access + * @throws \moodle_exception when the user has no access */ public static function assert_admin_access() { - if (!has_capability(user_helper::CAPABILITY_ADMIN, \context_system::instance()) && - !has_capability(user_helper::CAPABILITY_MANAGER, \context_system::instance())) { + if (!has_capability(CAPABILITY::ADMIN, \context_system::instance()) + && !has_capability(CAPABILITY::MANAGER, \context_system::instance()) + ) { throw new \moodle_exception('Acces denied'); } } - public static function get_all_feedbacks() : array { + /** + * Returns all feedback records. + * + * @return array all feedback records + */ + public static function get_all_feedbacks(): array { global $DB; return $DB->get_records(self::LBPLANNER_FEEDBACK_TABLE); } diff --git a/lbplanner/classes/helpers/invite_helper.php b/lbplanner/classes/helpers/invite_helper.php new file mode 100644 index 00000000..184cdfe7 --- /dev/null +++ b/lbplanner/classes/helpers/invite_helper.php @@ -0,0 +1,49 @@ +. + +namespace local_lbplanner\helpers; + +use external_single_structure; +use external_value; + +/** + * Helper class for plan invites + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class invite_helper { + + /** + * Returns the data structure of an invite. + * + * @return external_single_structure The data structure of an invite. + */ + public static function structure(): external_single_structure { + return new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'invite ID'), + 'inviterid' => new external_value(PARAM_INT, 'ID of the user who issued the invitation'), + 'inviteeid' => new external_value(PARAM_INT, 'ID of the user who got invited'), + 'planid' => new external_value(PARAM_INT, 'plan ID'), + 'status' => new external_value(PARAM_INT, 'status of the invitation'), + 'timestamp' => new external_value(PARAM_INT, 'time when the invitation was send'), + ] + ); + } +} diff --git a/lbplanner/classes/helpers/modules_helper.php b/lbplanner/classes/helpers/modules_helper.php index 99cb0278..b3500e08 100644 --- a/lbplanner/classes/helpers/modules_helper.php +++ b/lbplanner/classes/helpers/modules_helper.php @@ -14,6 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Collection of helper classes for handling modules + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace local_lbplanner\helpers; use block_accessreview\external\get_module_data; @@ -21,6 +30,7 @@ use external_single_structure; use external_value; use moodle_url; +use local_lbplanner\enums\{MODULE_STATUS, MODULE_GRADE, MODULE_TYPE}; /** * Contains helper functions for working with modules. @@ -62,109 +72,38 @@ class modules_helper { */ const SUBMISSION_STATUS_SUBMITTED = 'submitted'; - /** - * Enum value for grade 'Nicht erfüllt'. - */ - const GRADE_RIP = 4; - - /** - * Enum value for grade 'GK überwiegend'. - */ - const GRADE_GK = 3; - - /** - * Enum value for grade 'GK vollständig'. - */ - const GRADE_GKV = 2; - - - /** - * Enum value for grade 'EK überwiegend'. - */ - const GRADE_EK = 1; - - /** - * Enum value for grade 'EK vollständig'. - */ - const GRADE_EKV = 0; - - /** - * Enum value for completed module. - */ - const STATUS_DONE = 0; - - /** - * Enum value for uploaded module. - */ - const STATUS_UPLOADED = 1; - - /** - * Enum value for not completed module. - */ - const STATUS_LATE = 2; - - /** - * Enum value for pending module. - */ - const STATUS_PENDING = 3; - - /** - * Enum value for modules of type 'GK'. - */ - const TYPE_GK = 0; - - /** - * Enum value for modules of type 'EK'. - */ - const TYPE_EK = 1; - - /** - * Enum value for modules of type 'TEST'. - */ - const TYPE_TEST = 2; - - /** - * Enum value for modules of type 'M'. - */ - const TYPE_M = 3; - - /** - * Enum value for non - */ - const TYPE_NONE = 4; - /** * The return structure of a module. * * @return external_single_structure The structure of a module. */ - public static function structure() : external_single_structure { + public static function structure(): external_single_structure { return new external_single_structure( - array( - 'moduleid' => new external_value(PARAM_INT, 'The id of the module'), - 'name' => new external_value(PARAM_TEXT, 'The name of the module'), - 'courseid' => new external_value(PARAM_INT, 'The id of the course'), - 'status' => new external_value(PARAM_INT, 'The status of the module'), - 'type' => new external_value(PARAM_INT, 'The type of the module'), - 'url' => new external_value(PARAM_TEXT, 'The url of the module in moodle'), - 'grade' => new external_value(PARAM_INT, 'The grade of the module'), + [ + 'moduleid' => new external_value(PARAM_INT, 'Module ID'), + 'name' => new external_value(PARAM_TEXT, 'Shortened module name (max. 5 chars)'), + 'courseid' => new external_value(PARAM_INT, 'Course ID'), + 'status' => new external_value(PARAM_INT, 'Module status '.MODULE_STATUS::format()), + 'type' => new external_value(PARAM_INT, 'Module type '.MODULE_TYPE::format()), + 'url' => new external_value(PARAM_TEXT, 'URL to moodle page for module'), + 'grade' => new external_value(PARAM_INT, 'The grade of the module '.MODULE_GRADE::format()), 'deadline' => new external_value(PARAM_INT, 'The deadline of the module set by the teacher'), - ) + ] ); } /** * Determins the enum value for a grade. * - * @param integer $grade The grade of the module. - * @param integer $maxgrade The max. grade of the module. - * @param integer $mingrade The min. grade of the module. - * @param integer $gradepass The grade to pass the module. + * @param int $grade The grade of the module. + * @param int $maxgrade The max. grade of the module. + * @param int $mingrade The min. grade of the module. + * @param int $gradepass The grade to pass the module. * @return integer The enum value for the grade. */ - public static function determin_uinified_grade(int $grade, int $maxgrade, int $mingrade, int $gradepass) : int { + public static function determin_uinified_grade(int $grade, int $maxgrade, int $mingrade, int $gradepass): int { if ($grade < $gradepass) { - return self::GRADE_RIP; + return MODULE_GRADE::RIP; } $maxgrade = $maxgrade - $mingrade; @@ -172,35 +111,35 @@ public static function determin_uinified_grade(int $grade, int $maxgrade, int $m $p = $grade / $maxgrade; if ($p >= 0.9) { - return self::GRADE_EKV; + return MODULE_GRADE::EKV; } else if ($p >= 0.8) { - return self::GRADE_EK; + return MODULE_GRADE::EK; } else if ($p >= 0.7) { - return self::GRADE_GKV; + return MODULE_GRADE::GKV; } else if ($p >= 0.4) { - return self::GRADE_GK; + return MODULE_GRADE::GK; } else { - return self::GRADE_RIP; + return MODULE_GRADE::RIP; } } /** * Maps the given info to a module status. * - * @param boolean $submitted Whether the module is submitted. - * @param boolean $done Whether the module is completed. - * @param boolean $late Whether the module is late. + * @param bool $submitted Whether the module is submitted. + * @param bool $done Whether the module is completed. + * @param bool $late Whether the module is late. * @return integer The enum value for the module status. */ - public static function map_status(bool $submitted, bool $done, bool $late) : int { + public static function map_status(bool $submitted, bool $done, bool $late): int { if ($done) { - return self::STATUS_DONE; + return MODULE_STATUS::DONE; } else if ($submitted) { - return self::STATUS_UPLOADED; + return MODULE_STATUS::UPLOADED; } else if ($late) { - return self::STATUS_LATE; + return MODULE_STATUS::LATE; } else { - return self::STATUS_PENDING; + return MODULE_STATUS::PENDING; } } @@ -210,46 +149,46 @@ public static function map_status(bool $submitted, bool $done, bool $late) : int * @param string $modulename The name of the module. * @return integer The enum value for the module type. */ - public static function determin_type(string $modulename) : int { + public static function determin_type(string $modulename): int { // Convert module name to uppercase. $modulename = strtoupper($modulename); // Return TYPE_TEST if the name contains 'test' or 'sa'. if (strpos($modulename, '[TEST]') !== false || strpos($modulename, '[SA]') !== false) { - return self::TYPE_TEST; + return MODULE_TYPE::TEST; } // Return TYPE_GK if the name contains 'GK'. if (strpos($modulename, '[GK]') !== false) { - return self::TYPE_GK; + return MODULE_TYPE::GK; } if (strpos($modulename, '[EK]') !== false) { - return self::TYPE_EK; + return MODULE_TYPE::EK; } // Return TYPE_EK if the name contains 'M'. if (strpos($modulename, '[M]') !== false) { - return self::TYPE_M; + return MODULE_TYPE::M; } // Return TYPE_NONE elswise. - return self::TYPE_NONE; + return MODULE_TYPE::NONE; } /** * Returns the url of the module. * - * @param integer $moduleid The id of the module. - * @param integer $courseid The id of the course. + * @param int $moduleid The id of the module. + * @param int $courseid The id of the course. * @return string The url of the module. */ - public static function get_module_url(int $moduleid, int $courseid) : string { + public static function get_module_url(int $moduleid, int $courseid): string { global $DB; $view = $DB->get_record( self::COURSE_MODULES_TABLE, - array('course' => $courseid, 'instance' => $moduleid, 'module' => 1) + ['course' => $courseid, 'instance' => $moduleid, 'module' => 1] ); return strval(new moodle_url('/mod/assign/view.php?id='.$view->id)); @@ -258,31 +197,31 @@ public static function get_module_url(int $moduleid, int $courseid) : string { /** * Retrieves a module of the given id for the given user. * - * @param integer $moduleid The id of the module. - * @param integer $userid The id of the user. + * @param int $moduleid The id of the module. + * @param int $userid The id of the user. * @return array The module. */ - public static function get_module(int $moduleid, int $userid) : array { + public static function get_module(int $moduleid, int $userid): array { global $DB; date_default_timezone_set('UTC'); // Get module data. - $module = $DB->get_record(self::ASSIGN_TABLE, array('id' => $moduleid)); + $module = $DB->get_record(self::ASSIGN_TABLE, ['id' => $moduleid]); // Determine module type. $type = self::determin_type($module->name); - if ($type == self::TYPE_NONE) { - return array(); + if ($type == MODULE_TYPE::NONE) { + return []; } // Check if there are any submissions or feedbacks for this module. $submitted = false; - if ($DB->record_exists(self::SUBMISSIONS_TABLE, array('assignment' => $moduleid, 'userid' => $userid))) { + if ($DB->record_exists(self::SUBMISSIONS_TABLE, ['assignment' => $moduleid, 'userid' => $userid])) { $submission = $DB->get_record( self::SUBMISSIONS_TABLE, - array('assignment' => $moduleid, 'userid' => $userid) + ['assignment' => $moduleid, 'userid' => $userid] ); $submitted = strval($submission->status) == self::SUBMISSION_STATUS_SUBMITTED; @@ -291,12 +230,12 @@ public static function get_module(int $moduleid, int $userid) : array { $done = false; $grade = null; - if ($DB->record_exists(self::GRADES_TABLE, array('assignment' => $moduleid, 'userid' => $userid))) { - $moduleboundaries = $DB->get_record(self::GRADE_ITEMS_TABLE, array('iteminstance' => $moduleid)); + if ($DB->record_exists(self::GRADES_TABLE, ['assignment' => $moduleid, 'userid' => $userid])) { + $moduleboundaries = $DB->get_record(self::GRADE_ITEMS_TABLE, ['iteminstance' => $moduleid]); $mdlgrades = $DB->get_records( self::GRADES_TABLE, - array('assignment' => $moduleid, 'userid' => $userid) + ['assignment' => $moduleid, 'userid' => $userid] ); $mdlgrade = end($mdlgrades); @@ -310,7 +249,7 @@ public static function get_module(int $moduleid, int $userid) : array { $moduleboundaries->gradepass ); - $done = $grade != self::GRADE_RIP; + $done = $grade != MODULE_GRADE::RIP; } } // Check if the module is late. @@ -318,8 +257,8 @@ public static function get_module(int $moduleid, int $userid) : array { $late = false; $planid = plan_helper::get_plan_id($userid); - if ($DB->record_exists(plan_helper::DEADLINES_TABLE, array('planid' => $planid, 'moduleid' => $moduleid))) { - $deadline = $DB->get_record(plan_helper::DEADLINES_TABLE, array('planid' => $planid, 'moduleid' => $moduleid)); + if ($DB->record_exists(plan_helper::DEADLINES_TABLE, ['planid' => $planid, 'moduleid' => $moduleid])) { + $deadline = $DB->get_record(plan_helper::DEADLINES_TABLE, ['planid' => $planid, 'moduleid' => $moduleid]); $late = intval(date("Ymd", $deadline->deadlineend)) < intval(date("Ymd")) && !$done; } @@ -327,7 +266,7 @@ public static function get_module(int $moduleid, int $userid) : array { // Return the appropriate data. - return array( + return [ 'moduleid' => $moduleid, 'name' => $module->name, 'courseid' => $module->course, @@ -336,25 +275,26 @@ public static function get_module(int $moduleid, int $userid) : array { 'url' => self::get_module_url($moduleid, $module->course), 'grade' => $grade, 'deadline' => $module->duedate > 0 ? $module->duedate : null, - ); + ]; } /** * Reteruns all modules for the given course id. * - * @param integer $courseid The id of the course. - * @param integer $userid The id of the user. + * @param int $courseid The id of the course. + * @param int $userid The id of the user. + * @param bool $ekenabled Whether EK modules should be included. * @return array The modules. */ - public static function get_all_course_modules(int $courseid, int $userid, bool $ekenabled) : array { + public static function get_all_course_modules(int $courseid, int $userid, bool $ekenabled): array { global $DB; - $mdlmodules = $DB->get_records(self::ASSIGN_TABLE, array('course' => $courseid)); + $mdlmodules = $DB->get_records(self::ASSIGN_TABLE, ['course' => $courseid]); - $modules = array(); + $modules = []; foreach ($mdlmodules as $mdlmodule) { - if (!$ekenabled && self::determin_type($mdlmodule->name) == self::TYPE_EK) { + if (!$ekenabled && self::determin_type($mdlmodule->name) == MODULE_TYPE::EK) { continue; } $module = self::get_module($mdlmodule->id, $userid); diff --git a/lbplanner/classes/helpers/notifications_helper.php b/lbplanner/classes/helpers/notifications_helper.php index f7a40964..2d2f82e5 100644 --- a/lbplanner/classes/helpers/notifications_helper.php +++ b/lbplanner/classes/helpers/notifications_helper.php @@ -14,77 +14,69 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Provides helper classes for notification related stuff + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace local_lbplanner\helpers; +use external_single_structure; +use external_value; +use local_lbplanner\enums\{NOTIF_STATUS, NOTIF_TRIGGER}; + /** - * Provides helper methods for notification related stuff. + * Provides helper methods for notification related stuff */ class notifications_helper { /** * Name of the notifications table. */ - const TABLE = 'local_lbplanner_notification'; - - /** - * Enum value for a read notification. - */ - const STATUS_READ = 1; - - /** - * Enum value for a unread notification. - */ - const STATUS_UNREAD = 0; - - /** - * Enum value for a notification triggered by an invitation. - */ - const TRIGGER_INVITE = 0; - - /** - * Enum value for a notification triggered by an acceptation of an invitation. - */ - const TRIGGER_INVITE_ACCEPTED = 1; - - /** - * Enum value for a notification triggered by a rejection of an invitation. - */ - const TRIGGER_INVITE_DECLINED = 2; - - /** - * Enum value for a notification triggered by a user leaving a plan. - */ - const TRIGGER_PLAN_LEFT = 3; - - /** - * Enum value for a notification triggered by a user being removed from a plan. - */ - const TRIGGER_PLAN_REMOVED = 4; + const LBPLANNER_NOTIFICATION_TABLE = 'local_lbplanner_notification'; /** - * Enum value for a notification triggered by a new user. + * Returns the data structure of a notification + * + * @return external_single_structure The data structure of a notification. */ - const TRIGGER_USER_REGISTERED = 5; + public static function structure(): external_single_structure { + return new external_single_structure([ + 'status' => new external_value(PARAM_INT, 'The status of the notification ' . NOTIF_STATUS::format()), + 'type' => + new external_value(PARAM_INT, 'The type of the event that triggered the notification ' . NOTIF_TRIGGER::format()), + 'info' => new external_value(PARAM_INT, 'Additional information about the notification'), + 'userid' => new external_value(PARAM_INT, 'The ID of the user for whom the notification is for'), + 'notificationid' => new external_value(PARAM_INT, 'The ID of the notification', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + 'timestamp' => new external_value(PARAM_INT, 'The timestamp of the notification'), + 'timestamp_read' => new external_value(PARAM_INT, 'The timestamp of the notification when it was read'), + ]); + } /** * Notifies the given user about the given event, with the given info. * - * @param integer $userid The user to notify. - * @param integer $info Additional information as stringified json. - * @param integer $type The type of notification. + * @param int $userid The user to notify. + * @param int $info Additional information as stringified json. + * @param int $type The type of notification. + * * @return integer The id of the notification. */ - public static function notify_user( int $userid, int $info, int $type ): int { + public static function notify_user(int $userid, int $info, int $type): int { global $DB; $notification = new \stdClass(); $notification->userid = $userid; $notification->info = $info; $notification->type = $type; - $notification->status = self::STATUS_UNREAD; + $notification->status = NOTIF_STATUS::UNREAD; $notification->timestamp = time(); $notification->timestamp_read = null; - $id = $DB->insert_record(self::TABLE, $notification); + $id = $DB->insert_record(self::LBPLANNER_NOTIFICATION_TABLE, $notification); return $id; } diff --git a/lbplanner/classes/helpers/plan_helper.php b/lbplanner/classes/helpers/plan_helper.php index eee5da31..63b803b6 100644 --- a/lbplanner/classes/helpers/plan_helper.php +++ b/lbplanner/classes/helpers/plan_helper.php @@ -14,66 +14,26 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Provides helper classes for any tables related with the planning function of the app + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + namespace local_lbplanner\helpers; use external_single_structure; use external_value; use external_multiple_structure; +use local_lbplanner\enums\PLAN_ACCESS_TYPE; /** - * Provides helper methods for any tables related with the planning function of the app. + * Provides helper methods for any tables related with the planning function of the app */ class plan_helper { - /** - * Enum value for the plan owner. - */ - const ACCESS_TYPE_OWNER = 0; - - /** - * Enum value for plan members with write access to the plan. - */ - const ACCESS_TYPE_WRITE = 1; - - /** - * Enum value for plan members with read access to the plan. - */ - const ACCESS_TYPE_READ = 2; - - /** - * Enum value for plan members with no access to the plan. - */ - const ACCESS_TYPE_NONE = -1; - - /** - * Boolean value for ek-modules disabled. - */ - const EK_DISABLED = 0; - - /** - * Boolean value for ek-modules enabled. - */ - const EK_ENABLED = 1; - - /** - * Enum value for invites: The invite is pending and has not been accepted yet. - */ - const INVITE_PENDING = 0; - - /** - * Enum value for invites: The invite has been accepted. - */ - const INVITE_ACCEPTED = 1; - - /** - * Enum value for invites: The invite has been declined. - */ - const INVITE_DECLINED = 2; - - /** - * Enum value for invites: The invite has expired. - */ - const INVITE_EXPIRED = 3; - /** * local_lbplanner_plans table. */ @@ -97,27 +57,29 @@ class plan_helper { /** * Returns a list of user id's that are members of the plan. * - * @param integer $planid The id of the plan. + * @param int $planid The id of the plan. + * * @return array An array of user id's. */ - public static function get_plan_members(int $planid):array { + public static function get_plan_members(int $planid): array { global $DB; - $members = $DB->get_records(self::ACCESS_TABLE, array('planid' => $planid)); + $members = $DB->get_records(self::ACCESS_TABLE, ['planid' => $planid]); return $members; } /** * Returns the user id of the owner of the plan. * - * @param integer $planid The id of the plan. + * @param int $planid The id of the plan. + * * @return integer The user id of the owner. */ - public static function get_owner(int $planid):int { + public static function get_owner(int $planid): int { global $DB; $owner = $DB->get_field( self::ACCESS_TABLE, - 'userid', array('planid' => $planid, 'accesstype' => self::ACCESS_TYPE_OWNER) + 'userid', ['planid' => $planid, 'accesstype' => PLAN_ACCESS_TYPE::OWNER] ); return $owner; @@ -126,65 +88,74 @@ public static function get_owner(int $planid):int { /** * Returns the id of the plan that the given user is a member of. * - * @param integer $userid The id of the user. + * @param int $userid The id of the user. + * * @return integer The id of the plan the given user is a member of. */ - public static function get_plan_id(int $userid):int { + public static function get_plan_id(int $userid): int { global $DB; - $planid = $DB->get_field(self::ACCESS_TABLE, 'planid', array('userid' => $userid)); + $planid = $DB->get_field(self::ACCESS_TABLE, 'planid', ['userid' => $userid]); return $planid; } /** * Returns the access type of the given user for the given plan. * - * @param integer $planid The id of the plan. - * @param integer $userid The id of the user. - * @return integer The access type of the given user for the given plan. + * @param int $userid The id of the user. + * @param int $planid The id of the plan. + * + * @return int The access type of the given user for the given plan. */ - public static function get_access_type(int $userid, int $planid):int { + public static function get_access_type(int $userid, int $planid): int { global $DB; - if ($DB->record_exists(self::ACCESS_TABLE, array('planid' => $planid, 'userid' => $userid))) { - return $DB->get_field(self::ACCESS_TABLE, 'accesstype', array('planid' => $planid, 'userid' => $userid)); + $field = $DB->get_field( + self::ACCESS_TABLE, + 'accesstype', + ['planid' => $planid, 'userid' => $userid] + ); + + if ($field === false) { + return PLAN_ACCESS_TYPE::NONE; } else { - return self::ACCESS_TYPE_NONE; + return PLAN_ACCESS_TYPE::from($field); } } /** * Checks if the given user has editing permissions for the given plan. * - * @param integer $planid The id of the plan. - * @param integer $userid The id of the user. + * @param int $planid The id of the plan. + * @param int $userid The id of the user. + * * @return boolean True if the given user has editing permissions for the given plan. */ - public static function check_edit_permissions(int $planid, int $userid):bool { + public static function check_edit_permissions(int $planid, int $userid): bool { $access = self::get_access_type($userid, $planid); - return $access == self::ACCESS_TYPE_OWNER || $access == self::ACCESS_TYPE_WRITE; + return $access === PLAN_ACCESS_TYPE::OWNER || $access === PLAN_ACCESS_TYPE::WRITE; } /** * Returns a list of all deadlines for the given plan. * - * @param integer $planid The id of the plan. + * @param int $planid The id of the plan. + * * @return array A list of all deadlines for the given plan. */ public static function get_deadlines(int $planid): array { global $DB; - $dbdeadlines = $DB->get_records(self::DEADLINES_TABLE, array('planid' => $planid)); + $dbdeadlines = $DB->get_records(self::DEADLINES_TABLE, ['planid' => $planid]); - $deadlines = array(); + $deadlines = []; foreach ($dbdeadlines as $dbdeadline) { - $deadlines[] = array( - 'planid' => $dbdeadline->planid, + $deadlines[] = [ 'deadlinestart' => $dbdeadline->deadlinestart, 'deadlineend' => $dbdeadline->deadlineend, 'moduleid' => $dbdeadline->moduleid, - ); + ]; } return $deadlines; @@ -193,29 +164,30 @@ public static function get_deadlines(int $planid): array { /** * Retrieves all the information available about the given plan. * - * @param integer $planid The id of the plan. + * @param int $planid The id of the plan. + * * @return array An array containing all the information available about the given plan. */ - public static function get_plan(int $planid) : array { + public static function get_plan(int $planid): array { global $DB; - $plan = $DB->get_record(self::TABLE, array('id' => $planid)); - $members = array(); + $plan = $DB->get_record(self::TABLE, ['id' => $planid]); + $members = []; foreach (self::get_plan_members($planid) as $member) { - $members[] = array( + $members[] = [ 'userid' => $member->userid, 'accesstype' => $member->accesstype, - ); + ]; } - return array( + return [ 'name' => $plan->name, 'planid' => $planid, 'enableek' => $plan->enableek, 'deadlines' => self::get_deadlines($planid), 'members' => $members, - ); + ]; } /** @@ -223,72 +195,70 @@ public static function get_plan(int $planid) : array { * * @return external_single_structure The structure. */ - public static function plan_structure() : external_single_structure { + public static function plan_structure(): external_single_structure { return new external_single_structure( - array( - 'name' => new external_value(PARAM_TEXT, 'The name of the plan'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan'), - 'enableek' => new external_value(PARAM_BOOL, 'If the plan is enabled for ek'), + [ + 'name' => new external_value(PARAM_TEXT, 'Name of the plan'), + 'planid' => new external_value(PARAM_INT, 'ID of the plan'), + 'enableek' => new external_value(PARAM_BOOL, 'Whether EK is enabled'), 'deadlines' => new external_multiple_structure( new external_single_structure( - array( - 'planid' => new external_value(PARAM_INT, 'The id of the user'), - 'moduleid' => new external_value(PARAM_INT, 'The id of the user'), - 'deadlinestart' => new external_value(PARAM_INT, 'The id of the user'), - 'deadlineend' => new external_value(PARAM_INT, 'The id of the user'), - ) + [ + 'moduleid' => new external_value(PARAM_INT, 'ID of the module'), + 'deadlinestart' => new external_value(PARAM_INT, 'Start of the deadline as an UNIX timestamp'), + 'deadlineend' => new external_value(PARAM_INT, 'End of the deadline as an UNIX timestamp'), + ] ) ), 'members' => new external_multiple_structure( new external_single_structure( - array( + [ 'userid' => new external_value(PARAM_INT, 'The id of the user'), - 'accesstype' => new external_value(PARAM_INT, 'The role of the user'), - ) + 'accesstype' => new external_value(PARAM_INT, 'The role of the user ' . PLAN_ACCESS_TYPE::format()), + ] ) ), - ) + ] ); } /** * Copies the given plan to the given user. * - * @param integer $planid The id of the plan. - * @param integer $userid The id of the user. + * @param int $planid The id of the plan. + * @param string $username Username of the user. + * * @return integer The id of the new copy of the plan. */ - public static function copy_plan(int $planid, int $userid) : int { + public static function copy_plan(int $planid, string $username): int { global $DB; - $user = user_helper::get_mdl_user_info($userid); - - $plan = $DB->get_record(self::TABLE, array('id' => $planid)); - $plan->name = $plan->name . ' (' . $user->username . ')'; + $plan = $DB->get_record(self::TABLE, ['id' => $planid]); + $plan->name = $plan->name . ' (' . $username . ')'; $plan->id = null; $deadlines = self::get_deadlines($planid); $newplanid = $DB->insert_record(self::TABLE, $plan); - // Had to do it with insert and then update because the Variable didnt change in the Loop. - // I don't know why. It just works, so dont touch it 🚧. foreach ($deadlines as $deadline) { - $id = $DB->insert_record(self::DEADLINES_TABLE, $deadline); - $DB->update_record(self::DEADLINES_TABLE, array('id' => $id, 'planid' => $newplanid)); + $deadline['planid'] = $newplanid; + $DB->insert_record(self::DEADLINES_TABLE, $deadline); } return $newplanid; } + /** * Removes the user from the given plan. * - * @param integer $planid the plan id. - * @param integer $userid the user id. - * @param integer $removeuserid the user id to remove. - * @return array An array containing the new id of the plan + * @param int $planid the plan id. + * @param int $userid the user id. + * @param int $removeuserid the user id to remove. + * + * @return int The ID of the new plan for the removed user */ - public static function remove_user(int $planid, int $userid, int $removeuserid) : array { - global $DB; + public static function remove_user(int $planid, int $userid, int $removeuserid): int { + global $DB; // TODO: use global user object. if (self::get_owner($planid) != $userid) { throw new \moodle_exception('Access denied'); } @@ -301,32 +271,50 @@ public static function remove_user(int $planid, int $userid, int $removeuserid) throw new \moodle_exception('Cannot remove yourself'); } - if (self::get_access_type($removeuserid, $planid) == self::ACCESS_TYPE_OWNER) { + if (self::get_access_type($removeuserid, $planid) === PLAN_ACCESS_TYPE::OWNER) { throw new \moodle_exception('Cannot remove owner'); } - $newplanid = self::copy_plan($planid, $removeuserid); + $newplanid = self::copy_plan($planid, user_helper::get_mdluser($removeuserid)->username); $oldaccess = $DB->get_record( self::ACCESS_TABLE, - array('planid' => $planid, 'userid' => $removeuserid), '*', MUST_EXIST + ['planid' => $planid, 'userid' => $removeuserid], '*', MUST_EXIST ); $oldaccess->planid = $newplanid; - $oldaccess->accesstype = self::ACCESS_TYPE_OWNER; + $oldaccess->accesstype = PLAN_ACCESS_TYPE::OWNER; $DB->update_record(self::ACCESS_TABLE, $oldaccess); - return self::get_plan($planid, $removeuserid); + return $newplanid; } - public static function get_invites_send(int $userid):array { + + /** + * Get all invites that have been sent by the user. + * TODO: move to notifications_helper + * + * @param int $userid ID of the sender + * + * @return array an array of invites sent by the user + */ + public static function get_invites_send(int $userid): array { global $DB; - $invites = $DB->get_records(self::INVITES_TABLE, array('inviterid' => $userid)); + $invites = $DB->get_records(self::INVITES_TABLE, ['inviterid' => $userid]); return $invites; } - public static function get_invites_received(int $userid):array { + + /** + * Get all invites that have been received by the user. + * TODO: move to notifications_helper + * + * @param int $userid ID of the receiver + * + * @return array an array of invites received by the user + */ + public static function get_invites_received(int $userid): array { global $DB; - $invites = $DB->get_records(self::INVITES_TABLE, array('inviteeid' => $userid)); + $invites = $DB->get_records(self::INVITES_TABLE, ['inviteeid' => $userid]); return $invites; } } diff --git a/lbplanner/classes/helpers/slot_helper.php b/lbplanner/classes/helpers/slot_helper.php new file mode 100644 index 00000000..16a52cbd --- /dev/null +++ b/lbplanner/classes/helpers/slot_helper.php @@ -0,0 +1,310 @@ +. + +/** + * Provides helper classes for any tables related with the slot booking function of the app + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\helpers; + +use DateInterval; +use DateTime; +use DateTimeImmutable; +use DateTimeInterface; +use local_lbplanner\enums\WEEKDAY; +use local_lbplanner\model\{slot, reservation, slot_filter}; + +/** + * Provides helper methods for any tables related with the planning function of the app + */ +class slot_helper { + /** + * how far into the future a user can reserve a slot + */ + const RESERVATION_RANGE_USER = 3; + /** + * how far into the future a supervisor can reserve a slot for a user + */ + const RESERVATION_RANGE_SUPERVISOR = 7; + /** + * how long the room names can be in characters + * unicode characters might count as multiple characters + */ + const ROOM_MAXLENGTH = 7; // TODO: increase to 255 or sumn. + /** + * school units according to untis, in H:i format + */ + const SCHOOL_UNITS = [ + null, + '08:00', + '08:50', + '09:50', + '10:40', + '11:30', + '12:30', + '13:20', + '14:10', + '15:10', + '16:00', + '17:00', // All units after this point are 45min long instead of the usual 50. + '17:45', // We will assume 50min anyway because it's easier that way. + '18:45', + '19:30', + '20:15', + '21:00', + ]; + /** + * local_lbplanner_slots table. + */ + const TABLE_SLOTS = 'local_lbplanner_slots'; + /** + * local_lbplanner_reservations table. + */ + const TABLE_RESERVATIONS = 'local_lbplanner_reservations'; + /** + * local_lbplanner_slot_courses table. + */ + const TABLE_SLOT_FILTERS = 'local_lbplanner_slot_courses'; + /** + * local_lbplanner_supervisors table. + */ + const TABLE_SUPERVISORS = 'local_lbplanner_supervisors'; + + /** + * Returns a list of all slots. + * + * @return slot[] An array of the slots. + */ + public static function get_all_slots(): array { + global $DB; + $slots = $DB->get_records(self::TABLE_SLOTS, []); + + $slotsobj = []; + foreach ($slots as $slot) { + array_push($slotsobj, new slot(...$slot)); + } + + return $slotsobj; + } + + /** + * Returns a list of all slots belonging to a supervisor. + * @param int $supervisorid userid of the supervisor in question + * + * @return slot[] An array of the slots. + */ + public static function get_supervisor_slots(int $supervisorid): array { + global $DB; + + $slots = $DB->get_records_sql( + 'SELECT slot.* FROM {'.self::TABLE_SLOTS.'} as slot'. + 'INNER JOIN '.self::TABLE_SUPERVISORS.' as supervisor ON supervisor.slotid=slot.id'. + 'WHERE supervisor.userid=?', + [$supervisorid] + ); + + $slotsobj = []; + foreach ($slots as $slot) { + array_push($slotsobj, new slot(...$slot)); + } + + return $slotsobj; + } + + /** + * Returns a singular slot. + * @param int $slotid ID of the slot + * + * @return slot the requested slot + */ + public static function get_slot(int $slotid): slot { + global $DB; + $slot = $DB->get_record(self::TABLE_SLOTS, ['id' => $slotid]); + + return new slot(...$slot); + } + + /** + * Returns a singular reservation. + * @param int $reservationid ID of the reservation + * + * @return reservation the requested reservation + */ + public static function get_reservation(int $reservationid): reservation { + global $DB; + $reservation = $DB->get_record(self::TABLE_RESERVATIONS, ['id' => $reservationid]); + + if ($reservation === false) { + throw new \moodle_exception('requested reservation does not exist'); + } + + $reservation['date'] = new DateTimeImmutable($reservation['date']); + + return new reservation(...$reservation); + } + + /** + * Returns reservations for a slot. + * @param int $slotid ID of the slot + * + * @return reservation[] the requested reservations + */ + public static function get_reservations_for_slot(int $slotid): array { + global $DB; + $reservations = $DB->get_records(self::TABLE_RESERVATIONS, ['slotid' => $slotid]); + + $reservationsobj = []; + foreach ($reservations as $reservation) { + $reservation['date'] = new DateTimeImmutable($reservation['date']); + array_push($reservationsobj, new reservation(...$reservation)); + } + + return $reservationsobj; + } + + /** + * Returns filters for a slot. + * @param int $slotid ID of the slot + * + * @return slot_filter[] the requested filters + */ + public static function get_filters_for_slot(int $slotid): array { + global $DB; + $filters = $DB->get_records(self::TABLE_SLOT_FILTERS, ['slotid' => $slotid]); + + $filtersobj = []; + foreach ($filters as $filter) { + array_push($filtersobj, new slot_filter(...$filter)); + } + + return $filtersobj; + } + + /** + * Filters an array of slots for the slots that the user can theoretically reserve + * NOTE: not taking into account time or fullness, only filters i.e. users' class and courses + * TODO: replace $user with $vintage + * @param slot[] $allslots the slots to filter + * @param mixed $user a user object - e.g. $USER or a user object from the database + * @return slot[] the filtered slot array + */ + public static function filter_slots_for_user(array $allslots, mixed $user): array { + $mycourses = course_helper::get_all_lbplanner_courses(); + $mycourseids = []; + foreach ($mycourses as $course) { + array_push($mycourseids, $course->courseid); + } + + $slots = []; + foreach ($allslots as $slot) { + $filters = self::get_filters_for_slot($slot->id); + foreach ($filters as $filter) { + // Checking for course ID. + if (!is_null($filter->courseid) && !in_array($filter->courseid, $mycourseids)) { + continue; + } + // TODO: replace address with cohorts. + // Checking for vintage. + if (!is_null($filter->vintage) && $user->address !== $filter->vintage) { + continue; + } + // If all filters passed, add slot to my slots and break. + array_push($slots, $slot); + break; + } + } + return $slots; + } + + /** + * Filters an array of slots for a timerange around now. + * @param slot[] $allslots the slots to filter + * @param int $range how many days in the future the slot is allowed to be + * @return slot[] the filtered slot array + */ + public static function filter_slots_for_time(array $allslots, int $range): array { + $now = new DateTimeImmutable(); + $slots = []; + // Calculate date and time each slot happens next, and add it to the return list if within reach from today. + foreach ($allslots as $slot) { + $slotdatetime = self::calculate_slot_datetime($slot, $now); + + if ($now->diff($slotdatetime)->days <= $range) { + array_push($slots, $slot); + } + } + return $slots; + } + + /** + * calculates when a slot is to happen next + * @param slot $slot the slot + * @param DateTimeInterface $now the point in time representing now + * @return DateTimeImmutable the next time this slot will occur + */ + public static function calculate_slot_datetime(slot $slot, DateTimeInterface $now): DateTimeImmutable { + $slotdaytime = self::SCHOOL_UNITS[$slot->startunit]; + // NOTE: format and fromFormat use different date formatting conventions. + $slotdatetime = DateTime::createFromFormat('YY-MM-DD tHH:MM', $now->format('Y-m-d ').$slotdaytime); + // Move to next day this weekday occurs (doesn't move if it's the same as today). + $slotdatetime->modify('this '.WEEKDAY::name_from($slot->weekday)); + + // Check if slot is before now (because time of day and such) and move it a week into the future if so. + if ($now->diff($slotdatetime)->invert === 1) { + $slotdatetime->add(new DateInterval('P1W')); + } + + return new DateTimeImmutable($slotdatetime); + } + + /** + * Amends a date with time of day using the units system + * @param int $unit the unit to use + * @param DateTimeInterface $date the date (time of day will be ignored) + * + * @return DateTimeImmutable the new date with time of day filled in + * @link slot_helper::SCHOOL_UNITS + */ + public static function amend_date_with_unit_time(int $unit, DateTimeInterface $date): DateTimeImmutable { + $daytime = self::SCHOOL_UNITS[$unit]; + + return DateTimeImmutable::createFromFormat('YY-MM-DD tHH:MM', $date->format('Y-m-d ').$daytime); + } + + /** + * Checks whether a user is supervisor for a specific slot. + * @param int $supervisorid userid of the supervisor in question + * @param int $slotid the slot to check + * + * @return slot[] An array of the slots. + */ + public static function check_slot_supervisor(int $supervisorid, int $slotid): bool { + global $DB; + + $result = $DB->get_record_sql( + 'SELECT supervisor.userid FROM '.self::TABLE_SUPERVISORS.' as supervisor'. + 'WHERE supervisor.userid=? AND supervisor.slotid=?', + [$supervisorid, $slotid] + ); + + return $result !== false; + } +} diff --git a/lbplanner/classes/helpers/user_helper.php b/lbplanner/classes/helpers/user_helper.php index 01b2dee8..4fe6f701 100644 --- a/lbplanner/classes/helpers/user_helper.php +++ b/lbplanner/classes/helpers/user_helper.php @@ -16,104 +16,42 @@ namespace local_lbplanner\helpers; +use coding_exception; use context_system; -use moodle1_converter; -use moodle_database; -use moodle_url; -use moodleform; +use dml_exception; +use moodle_exception; use stdClass; +use user_picture; +use core_user; + +use local_lbplanner\enums\{CAPABILITY, CAPABILITY_FLAG}; +use local_lbplanner\model\user; /** * Provides helper methods for user related stuff. + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class user_helper { - /** - * Shortname of the admin CAPABILITY. - */ - const CAPABILITY_ADMIN = 'local/lb_planner:admin'; - - /** - * Shortname of the manager CAPABILITY. - */ - const CAPABILITY_MANAGER = 'local/lb_planner:manager'; - - /** - * Shortname of the teacher CAPABILITY. - */ - const CAPABILITY_TEACHER = 'local/lb_planner:teacher'; - - /** - * Shortname of the student CAPABILITY. - */ - const CAPABILITY_STUDENT = 'local/lb_planner:student'; - - /** - * Maps CAPABILITY shortnames to their corresponding enum value. - */ - const CAPABILITY_ENUMS = [ - self::CAPABILITY_ADMIN => 1, - self::CAPABILITY_MANAGER => 2, - self::CAPABILITY_TEACHER => 4, - self::CAPABILITY_STUDENT => 8 - ]; - /** * Name of the user database */ - const TABLE = 'local_lbplanner_users'; + const LB_PLANNER_USER_TABLE = 'local_lbplanner_users'; /** - * The table where moodle stores the user data. - */ - const MOODLE_TABLE = 'user'; - - /** - * The table where moodle stores the user context data. - */ - const MOODLE_CONTEXT_TABLE = 'context'; - - /** - * @deprecated Use user_helper::assert_access() instead * Checks if the current user has access to the given user id. * * @param int $userid The id of the user to check access for. + * * @return bool True if the current user has access to the given user id, false otherwise. */ - public static function check_access(int $userid):bool { + public static function check_access(int $userid): bool { global $USER; - return $USER->id == $userid; - } - - /** - * Retrieves the user with the given id. - * ```php - * $mdluser->username // The username of the user. - * $mdluser->firstname // The firstname of the user. - * $mdluser->lastname // The lastname of the user. - * $mdluser->profileimageurl // The profile image url of the user. - * ``` - * - * @param integer $userid The id of the user to retrieve. - * @return stdClass The user with the given id. - */ - public static function get_mdl_user_info(int $userid):stdClass { - global $DB; - $user = $DB->get_record(self::MOODLE_TABLE, array('id' => $userid), '*', MUST_EXIST); - $contextid = $DB->get_record( - self::MOODLE_CONTEXT_TABLE, - array('depth' => 2, 'contextlevel' => 30, 'instanceid' => $userid), - '*', - IGNORE_MISSING - ); - $mdluser = new stdClass(); - $mdluser->username = $user->username; - $mdluser->firstname = $user->firstname; - $mdluser->lastname = $user->lastname; - $mdluser->profileimageurl = strval(moodle_url::make_pluginfile_url($contextid->id, 'user', 'icon', null, '/boost_union/', 'f1.png')); - $mdluser->vintage = $user->address; - - return $mdluser; + return ((int) $USER->id) === $userid; } /** @@ -121,15 +59,30 @@ public static function get_mdl_user_info(int $userid):stdClass { * Throws an exception if the current user does not have access. * * @param int $userid The id of the user to check access for. + * * @return void + * @throws moodle_exception */ - public static function assert_access(int $userid) { - global $USER; - if ($USER->id != $userid) { - throw new \moodle_exception('Access denied'); + public static function assert_access(int $userid): void { + if (!self::check_access($userid)) { + throw new moodle_exception('Access denied'); } } + /** + * Checks if the given user is an admin. + * + * @param int $userid The id of the user to check. + * + * @return bool True if the given user is an admin, false otherwise. + * @throws coding_exception + * @throws dml_exception + */ + public static function is_admin(int $userid): bool { + $context = context_system::instance(); + return has_capability(CAPABILITY::ADMIN, $context, $userid, false); + } + /** * Gives back a bitmask which represents the capabilities of the given user. * 0 = no capabilities @@ -151,65 +104,70 @@ public static function assert_access(int $userid) { * * * @param int $userid The id of the user to check access for. + * * @return int The capabilities of the given user. + * @throws coding_exception + * @throws dml_exception */ - public static function get_user_capability_bitmask(int $userid) : int { - global $DB; + public static function get_user_capability_bitmask(int $userid): int { $capabilities = 0; $context = context_system::instance(); - if (has_capability(self::CAPABILITY_ADMIN, $context, $userid, false)) { - $capabilities += self::CAPABILITY_ENUMS[self::CAPABILITY_ADMIN]; + if (has_capability(CAPABILITY::ADMIN, $context, $userid, false)) { + $capabilities += CAPABILITY_FLAG::ADMIN; } - if (has_capability(self::CAPABILITY_MANAGER, $context, $userid, false)) { - $capabilities += self::CAPABILITY_ENUMS[self::CAPABILITY_MANAGER]; + if (has_capability(CAPABILITY::MANAGER, $context, $userid, false)) { + $capabilities += CAPABILITY_FLAG::MANAGER; } - if (has_capability(self::CAPABILITY_TEACHER, $context, $userid, false)) { - $capabilities += self::CAPABILITY_ENUMS[self::CAPABILITY_TEACHER]; + if (has_capability(CAPABILITY::TEACHER, $context, $userid, false)) { + $capabilities += CAPABILITY_FLAG::TEACHER; } - if (has_capability(self::CAPABILITY_STUDENT, $context, $userid, false)) { - $capabilities += self::CAPABILITY_ENUMS[self::CAPABILITY_STUDENT]; + if (has_capability(CAPABILITY::STUDENT, $context, $userid, false)) { + $capabilities += CAPABILITY_FLAG::STUDENT; } return $capabilities; } + /** - * Checks if the given user exists in the database. + * Checks if the given user exists in the LB_PLANNER_USER database. + * + * @param int $userid The id of the user to check. * - * @param integer $userid The id of the user to check. - * @return boolean True if the user exists, false otherwise. + * @return bool True if the user exists, false otherwise. + * @throws dml_exception */ public static function check_user_exists(int $userid): bool { global $DB; - return $DB->record_exists(self::TABLE, array('userid' => $userid)); + return $DB->record_exists(self::LB_PLANNER_USER_TABLE, ['userid' => $userid]); } /** * Retrieves the user with the given id. - * The returned object contains the following properties: - * ```php - * $user->id // The lbplanner id of the user. - * $user->userid // The moodle id of the user. - * $user->theme // The name of the theme the user has set for the app. - * $user->language // The language the user has set for the app. - * ``` * - * @param integer $userid The id of the user to retrieve. - * @return stdClass The user with the given id. + * @param int $userid The id of the user to retrieve. + * + * @return user The user with the given id. + * @throws dml_exception */ - public static function get_user(int $userid): stdClass { + public static function get_user(int $userid): user { global $DB; - return $DB->get_record(self::TABLE, array('userid' => $userid), '*', MUST_EXIST); + return user::from_db($DB->get_record(self::LB_PLANNER_USER_TABLE, ['userid' => $userid], '*', MUST_EXIST)); } /** - * Retrieves the full name of the user with the given id. + * Retrieves the user with the given id. + * + * @param int $userid The id of the user to retrieve. * - * @param integer $userid The id of the user to retrieve the full name for. - * @return string The full name of the user with the given id. + * @return \stdClass The user with the given id. + * @throws dml_exception */ - public static function get_complete_name(int $userid): string { - $user = self::get_mdl_user_info($userid); - - return $user->firstname . ' ' . $user->lastname; + public static function get_mdluser(int $userid): \stdClass { + global $USER; + if ($userid === ((int) $USER->id)) { + $data = $USER; + } else { + $data = core_user::get_user($userid, '*', MUST_EXIST); + } + return $data; } - } diff --git a/lbplanner/classes/model/course.php b/lbplanner/classes/model/course.php new file mode 100644 index 00000000..b0cccecd --- /dev/null +++ b/lbplanner/classes/model/course.php @@ -0,0 +1,249 @@ +. + +/** + * Model for a course + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +use external_single_structure; +use external_value; +use local_lbplanner\helpers\course_helper; + +/** + * Model class for course + */ +class course { + /** + * @var int $id course ID + */ + private int $id; + /** + * @var int $courseid the moodle-internal ID of the course + */ + public int $courseid; + /** + * @var int $userid the user for whom these course settings are for + */ + public int $userid; + /** + * @var string $shortname the short name of this course for this user + * maximum size: 5 chars + */ + public string $shortname; + /** + * @var string $fullname the full name of this course + */ + private ?string $fullname; + /** + * @var string $color the color for this course as #RRGGBB + */ + public string $color; + /** + * @var bool $enabled whether the user wants to see this course + */ + public bool $enabled; + + /** + * Constructs a new course + * @param int $id ID of course + * @param int $courseid ID of the moodle course + * @param int $userid ID of the user these settings are for + * @param string $shortname the short name for this course + * @param string $color the color for this course as #RRGGBB + * @param bool $enabled whether the course is enabled + */ + public function __construct(int $id, int $courseid, int $userid, string $shortname, string $color, bool $enabled) { + $this->id = $id; + $this->courseid = $courseid; + $this->userid = $userid; + $this->set_shortname($shortname); + $this->set_color($color); + $this->enabled = $enabled; + $this->fullname = null; + } + + /** + * Takes data from DB and makes a new Course out of it + * + * @param object $obj the DB object to get data from + * @return object a representation of this course and its data + * @throws \AssertionError + */ + public static function from_db(object $obj): self { + assert($obj->enabled === 0 || $obj->enabled === 1); + return new self($obj->id, $obj->courseid, $obj->userid, $obj->shortname, $obj->color, (bool) $obj->enabled); + } + + /** + * Mark the object as freshly created and sets the new ID + * @param int $id the new ID after inserting into the DB + * @throws \AssertionError + */ + public function set_fresh(int $id) { + assert($this->id === 0); + assert($id !== 0); + $this->id = $id; + } + + /** + * sets the color as #RRGGBB or #RGB in hexadecimal notation + * @param string $color the color + * @throws \coding_exception when the color format is wrong + */ + public function set_color(string $color) { + if ($color[0] !== '#') { + throw new \coding_exception("incorrect color format - must be either #RGB or #RRGGBB, got \"{$color}\" instead"); + } + $len = strlen($color); + if ($len === 4) { + // Transforming #RGB to #RRGGBB. + // This way, 0 corresponds to 00, and F to FF, meaning the full spectrum is used. + // This is also how Browsers handle it. + $rrggbb = $color[0] . $color[1] . $color[1] . $color[2] . $color[2] . $color[3] . $color[3]; + } else if ($len === 7) { + // Format #RRGGBB. + $rrggbb = $color; + } else { + throw new \coding_exception("incorrect color format - got incorrect length of {$len}"); + } + $rrggbb = strtoupper($rrggbb); + if (preg_match('/^#[1-9A-F]{6}$/', $rrggbb) === false) { + throw new \coding_exception("incorrect color format - found non-hexadecimal character in color \"{$color}\""); + } + $this->color = $rrggbb; + } + + /** + * sets the shortname + * @param string $shortname the shortname + * @throws \AssertionError + */ + public function set_shortname(string $shortname) { + assert(strlen($shortname) <= 5); + assert(strlen($shortname) > 0); + $this->shortname = $shortname; + } + + /** + * sets whether the course is enabled + * @param bool $enabled whether to enable the course + */ + public function set_enabled(bool $enabled) { + $this->enabled = $enabled; + } + + /** + * sets the cached fullname (mainly for deduplicating DB requests) + * @param string $fullname the cached fullname + */ + public function set_fullname(string $fullname) { + // If we already have a fullname stored and it's different, then we've found data inconsistency. + assert($this->fullname === null || $this->fullname === $fullname); + $this->fullname = $fullname; + } + + /** + * get the cached fullname + * @return string the cached fullname + */ + public function get_fullname(): string { + if ($this->fullname === null) { + $this->fullname = course_helper::get_fullname($this->courseid); + } + + return $this->fullname; + } + + /** + * Prepares a string to be eligible for shortname + * @param string $shortname the shortname to be prepared + * @return string the prepared shortname + */ + public static function prepare_shortname(string $shortname): string { + if (strpos($shortname, ' ') !== false) { + $shortname = substr($shortname, 0, strpos($shortname, ' ')); + } + if (strlen($shortname) >= 5) { + $shortname = substr($shortname, 0, 5); + } + return strtoupper($shortname); + } + + /** + * Prepares data for the DB endpoint. + * doesn't set ID if it's 0 + * + * @return object a representation of this course and its data + */ + public function prepare_for_db(): object { + $obj = new \stdClass(); + + $obj->courseid = $this->courseid; + $obj->userid = $this->userid; + $obj->shortname = $this->shortname; + $obj->color = $this->color; + $obj->enabled = $this->enabled ? 1 : 0; // The DB uses int instead of bool here. + + if ($this->id !== 0) { + $obj->id = $this->id; + } + + return $obj; + } + + /** + * Prepares data for the API endpoint. + * + * @return array a representation of this course and its data + */ + public function prepare_for_api(): array { + return [ + 'id' => $this->id, + 'courseid' => $this->courseid, + 'userid' => $this->userid, + 'name' => $this->get_fullname(), + 'shortname' => $this->shortname, + 'color' => $this->color, + 'enabled' => $this->enabled ? 1 : 0, // Moodle's API uses int instead of bool. + ]; + } + + /** + * Returns the data structure of a course for the API. + * + * @return external_single_structure The data structure of a course for the API. + */ + public static function api_structure(): external_single_structure { + return new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'course ID'), + 'courseid' => new external_value(PARAM_INT, 'moodle-internal course ID'), + 'userid' => new external_value(PARAM_INT, 'The user for whom these course settings are for'), + 'name' => new external_value(PARAM_TEXT, 'Full name of this course'), + 'shortname' => new external_value(PARAM_TEXT, 'Short name of this course for this user (maximum size: 5 chars)'), + 'color' => new external_value(PARAM_TEXT, 'Color for this course as #RRGGBB'), + 'enabled' => new external_value(PARAM_BOOL, 'Whether the user wants to see this course'), + ] + ); + } +} diff --git a/lbplanner/classes/model/reservation.php b/lbplanner/classes/model/reservation.php new file mode 100644 index 00000000..7f8ac6f6 --- /dev/null +++ b/lbplanner/classes/model/reservation.php @@ -0,0 +1,207 @@ +. + +/** + * Model for a reservation + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +use DateTimeImmutable; + +use external_single_structure; +use external_value; + +use local_lbplanner\model\slot; +use local_lbplanner\helpers\slot_helper; + +/** + * Model class for reservation + */ +class reservation { + /** + * @var int $id ID of reservation + */ + public int $id; + /** + * @var int $slotid ID of the linked slot + */ + public int $slotid; + /** + * @var DateTimeImmutable $date date this reservation is on (time will be ignored) + */ + public DateTimeImmutable $date; + /** + * @var int $userid ID of the user this reservation is for + */ + public int $userid; + /** + * @var int $reserverid ID of the user who submitted this reservation (either pupil or supervisor) + */ + public int $reserverid; + /** + * @var ?slot $slot the linked slot (gets filled in by helper functions) + */ + private ?slot $slot; + /** + * @var ?DateTimeImmutable $datetime the date this reservation starts at, with time filled in + */ + private ?DateTimeImmutable $datetime; + /** + * @var ?DateTimeImmutable $datetime the date this reservation ends at, with time filled in + */ + private ?DateTimeImmutable $datetimeend; + + /** + * Constructs a reservation + * @param int $id ID of reservation + * @param int $slotid ID of the linked slot + * @param DateTimeImmutable $date date this reservation is on (time will be ignored) + * @param int $userid ID of the user this reservation is for + * @param int $reserverid ID of the user who submitted this reservation (either pupil or supervisor) + * @link slot + */ + public function __construct(int $id, int $slotid, DateTimeImmutable $date, int $userid, int $reserverid) { + $this->id = $id; + $this->slotid = $slotid; + $this->date = $date; + $this->userid = $userid; + $this->reserverid = $reserverid; + $this->slot = null; + } + + /** + * Mark the object as freshly created and sets the new ID + * @param int $id the new ID after insertint into the DB + * @param ?slot $slot the cached slot object + */ + public function set_fresh(int $id, ?slot $slot) { + assert($this->id === 0); + assert($id !== 0); + $this->id = $id; + if (!is_null($slot)) { + $this->set_slot($slot); + } + } + + /** + * sets the cached slot object (mainly for deduplicating DB requests) + * @param slot $slot the cached slot object + */ + public function set_slot(slot $slot) { + assert($this->slotid === $slot->id); + $this->slot = $slot; + } + + /** + * Returns the associated slot. + * + * @return slot the associated slot + */ + public function get_slot(): slot { + if (is_null($this->slot)) { + $this->slot = slot_helper::get_slot($this->slotid); + } + + return $this->slot; + } + + /** + * Calculates the exact time and date this reservation is supposed to start + * + * @return DateTimeImmutable + */ + public function get_datetime(): DateTimeImmutable { + if (is_null($this->datetime)) { + $slot = $this->get_slot(); + $this->datetime = slot_helper::amend_date_with_unit_time($slot->startunit, $this->date); + } + + return $this->datetime; + } + + /** + * Calculates the exact time and date this reservation is supposed to start + * + * @return DateTimeImmutable + */ + public function get_datetime_end(): DateTimeImmutable { + if (is_null($this->datetime)) { + $slot = $this->get_slot(); + $this->datetimeend = slot_helper::amend_date_with_unit_time($slot->startunit + $slot->duration, $this->date); + } + + return $this->datetimeend; + } + + /** + * Prepares data for the DB endpoint. + * doesn't set ID if it's 0 + * + * @return object a representation of this reservation and its data + */ + public function prepare_for_db(): object { + $obj = new \stdClass(); + + $obj->slotid = $this->slotid; + $obj->date = $this->date; + $obj->userid = $this->userid; + $obj->reserverid = $this->reserverid; + + if ($this->id !== 0) { + $obj->id = $this->id; + } + + return $obj; + } + + /** + * Prepares data for the API endpoint. + * + * @return array a representation of this reservation and its data + */ + public function prepare_for_api(): array { + return [ + 'id' => $this->id, + 'slotid' => $this->slotid, + 'date' => $this->date->format('Y-m-d'), + 'userid' => $this->userid, + 'reserverid' => $this->reserverid, + ]; + } + + /** + * Returns the data structure of a reservation for the API. + * + * @return external_single_structure The data structure of a reservation for the API. + */ + public static function api_structure(): external_single_structure { + return new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'reservation ID'), + 'slotid' => new external_value(PARAM_INT, 'ID of associated slot'), + 'date' => new external_value(PARAM_TEXT, 'date of the reservation in YYYY-MM-DD (as per ISO-8601)'), + 'userid' => new external_value(PARAM_INT, 'ID of the user this reservation is for'), + 'reserverid' => new external_value(PARAM_INT, 'ID of the user who submitted this reservation'), + ] + ); + } +} diff --git a/lbplanner/classes/model/slot.php b/lbplanner/classes/model/slot.php new file mode 100644 index 00000000..4621d2c0 --- /dev/null +++ b/lbplanner/classes/model/slot.php @@ -0,0 +1,212 @@ +. + +/** + * Model for a slot + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +use local_lbplanner\enums\WEEKDAY; +use local_lbplanner\helpers\slot_helper; + +use external_single_structure; +use external_value; + +/** + * Model class for slot + */ +class slot { + /** + * @var int $id ID of slot + */ + public int $id; + /** + * @var int $startunit Unit this slot starts in + */ + public int $startunit; + /** + * @var int $duration duration of slot in units + */ + public int $duration; + /** + * @var int $weekday weekday this slot occurs in + */ + public int $weekday; + /** + * @var string $room room this slot is for + */ + public string $room; + /** + * @var int $size how many pupils fit in this slot + */ + public int $size; + /** + * @var ?int $fullness how many pupils have already reserved this slot (gets filled in by helper functions) + */ + private ?int $fullness; + /** + * @var ?bool $forcuruser whether the current user has reserved this slot (gets filled in by helper functions) + */ + private ?bool $forcuruser; + + /** + * Constructs a new Slot + * @param int $id ID of slot + * @param int $startunit Unit this slot starts in + * @param int $duration duration of slot in units + * @param int $weekday weekday this slot occurs in + * @param string $room room this slot is for + * @param int $size how many pupils fit in this slot + * @link slot_helper::SCHOOL_UNITS + * @link WEEKDAY + */ + public function __construct(int $id, int $startunit, int $duration, int $weekday, string $room, int $size) { + $this->id = $id; + assert($startunit > 0); + $this->startunit = $startunit; + assert($duration > 0); + assert($duration + $startunit < count(slot_helper::SCHOOL_UNITS)); + $this->duration = $duration; + $this->weekday = WEEKDAY::from($weekday); + assert(strlen($room) > 0 && strlen($room) <= slot_helper::ROOM_MAXLENGTH); + $this->room = $room; + assert($size >= 0); // Make it technically possible to not allow any students in a room to temporarily disable the slot. + $this->size = $size; + $this->fullness = null; + $this->forcuruser = null; + } + + /** + * Mark the object as freshly created and sets the new ID + * @param int $id the new ID after insertint into the DB + */ + public function set_fresh(int $id) { + assert($this->id === 0); + assert($id !== 0); + $this->id = $id; + $this->fullness = 0; + $this->forcuruser = false; + } + + /** + * Returns how many reservations there are for this slot. + * + * @return int fullness + */ + public function get_fullness(): int { + if (is_null($this->fullness)) { + $this->check_reservations(); + } + + return $this->fullness; + } + + /** + * Returns whether the current user has a reservation for this slot. + * + * @return bool forcuruser + */ + public function get_forcuruser(): bool { + if (is_null($this->forcuruser)) { + $this->check_reservations(); + } + + return $this->forcuruser; + } + + /** + * Prepares data for the DB endpoint. + * doesn't set ID if it's 0 + * + * @return object a representation of this slot and its data + */ + public function prepare_for_db(): object { + $obj = new \stdClass(); + + $obj->startunit = $this->startunit; + $obj->duration = $this->duration; + $obj->weekday = $this->weekday; + $obj->room = $this->room; + $obj->size = $this->size; + + if ($this->id !== 0) { + $obj->id = $this->id; + } + return $obj; + } + + /** + * Prepares data for the API endpoint. + * + * @return array a representation of this slot and its data + */ + public function prepare_for_api(): array { + return [ + 'id' => $this->id, + 'startunit' => $this->startunit, + 'duration' => $this->duration, + 'weekday' => $this->weekday, + 'room' => $this->room, + 'size' => $this->size, + 'fullness' => $this->get_fullness(), + 'forcuruser' => $this->get_forcuruser(), + ]; + } + + /** + * Returns the data structure of a slot for the API. + * + * @return external_single_structure The data structure of a slot for the API. + */ + public static function api_structure(): external_single_structure { + return new external_single_structure( + [ + 'id' => new external_value(PARAM_INT, 'slot ID'), + 'startunit' => new external_value(PARAM_INT, 'unit this slot starts in (8:00 is unit 1)'), + 'duration' => new external_value(PARAM_INT, 'duration of the slot in units'), + 'weekday' => new external_value(PARAM_INT, 'The day this unit repeats weekly: '.WEEKDAY::format()), + 'room' => new external_value(PARAM_TEXT, 'The room this slot is for'), + 'size' => new external_value(PARAM_INT, 'total capacity of the slot'), + 'fullness' => new external_value(PARAM_INT, 'how many people have already reserved this slot'), + 'forcuruser' => new external_value(PARAM_BOOL, 'whether the current user has reserved this slot'), + ] + ); + } + + /** + * Queries reservations for this slot and fills in internal data with that info. + */ + private function check_reservations(): void { + global $USER; + $reservations = slot_helper::get_reservations_for_slot($this->id); + + $this->fullness = count($reservations); + + foreach ($reservations as $reservation) { + if ($reservation->userid === $USER['id']) { + $this->forcuruser = true; + return; + } + } + $this->forcuruser = false; + } +} diff --git a/lbplanner/classes/model/slot_filter.php b/lbplanner/classes/model/slot_filter.php new file mode 100644 index 00000000..c732fb52 --- /dev/null +++ b/lbplanner/classes/model/slot_filter.php @@ -0,0 +1,65 @@ +. + +/** + * Model for a filter for slots + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +/** + * Model class for a filter for slots + */ +class slot_filter { + /** + * @var int $id ID of filter + */ + public int $id; + /** + * @var int $id ID of linked slot + */ + public int $slotid; + /** + * @var ?int $id ID of linked course or null if any + */ + public ?int $courseid; + /** + * @var ?string $vintage linked class or null if any + */ + public ?string $vintage; + + /** + * Constructs new slot_filter + * @param int $id ID of filter + * @param int $slotid ID of linked slot + * @param ?int $courseid ID of linked course or null if any + * @param ?string $vintage linked class or null if any + */ + public function __construct(int $id, int $slotid, ?int $courseid, ?string $vintage) { + $this->id = $id; + $this->slotid = $slotid; + $this->courseid = $courseid; + if (!is_null($vintage)) { + assert(strlen($vintage) <= 7); + } + $this->vintage = $vintage; + } +} diff --git a/lbplanner/classes/model/user.php b/lbplanner/classes/model/user.php new file mode 100644 index 00000000..88eefb61 --- /dev/null +++ b/lbplanner/classes/model/user.php @@ -0,0 +1,346 @@ +. + +/** + * Model for a course + * + * @package local_lbplanner + * @subpackage helpers + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\model; + +use coding_exception; +use external_single_structure; +use external_value; +use local_lbplanner\helpers\plan_helper; +use local_lbplanner\helpers\user_helper; +use user_picture; + +/** + * Model class for course + */ +class user { + /** + * @var int $lbpid our userid + */ + private int $lbpid; + + /** + * @var int $mdlid moodle's userid + */ + public int $mdlid; + + /** + * @var string $theme selected theme + */ + public string $theme; + + /** + * @var string $lang user language + */ + public string $lang; + + /** + * @var string $colorblindness the kind of color blindness of a user + */ + public string $colorblindness; + + /** + * @var int $displaytaskcount The display task count the user has selected in the app. + */ + public int $displaytaskcount; + + /** + * @var ?\stdClass $mdluser the cached moodle user + */ + private ?\stdClass $mdluser; + + /** + * @var ?string $pfp the cached pfp + */ + private ?string $pfp; + + /** + * @var ?int $planid the cached planid + */ + private ?int $planid; + + /** + * Constructs a new course + * @param int $lbpid ID of the lb planner user + * @param int $mdlid ID of the moodle user + * @param string $theme user-chosen theme + * @param string $lang user language + * @param string $colorblindness user's colorblindness + * @param int $displaytaskcount user's display task count + */ + public function __construct( + int $lbpid, + int $mdlid, + string $theme, + string $lang, + string $colorblindness, + int $displaytaskcount + ) { + global $USER; + $this->lbpid = $lbpid; + $this->mdlid = $mdlid; + $this->set_theme($theme); + $this->set_lang($lang); + $this->set_colorblindness($colorblindness); + $this->set_displaytaskcount($displaytaskcount); + $this->planid = null; + $this->pfp = null; + + if ($mdlid === (int) $USER->id) { + $this->mdluser = $USER; + } else { + $this->mdluser = null; + } + } + + /** + * Takes data from DB and makes a new user obj out of it + * + * @param object $obj the DB object to get data from + * @return user a representation of this user and its data + */ + public static function from_db(object $obj): self { + return new self($obj->id, $obj->userid, $obj->theme, $obj->language, $obj->colorblindness, $obj->displaytaskcount); + } + + /** + * Mark the object as freshly created and sets the new ID + * @param int $lbpid the new ID after inserting into the DB + * @throws \AssertionError + */ + public function set_fresh(int $lbpid): void { + assert($this->lbpid === 0); + assert($lbpid !== 0); + $this->lbpid = $lbpid; + } + + /** + * Sets display task count + * @param int $count display task count + * @throws \coding_exception if $count <= 0 + */ + public function set_displaytaskcount(int $count): void { + if ($count <= 0) { + throw new \coding_exception('User\'s Display Task Count cannot be <= 0'); + } + $this->displaytaskcount = $count; + } + + /** + * Sets colorblindness + * @param string $cbn colorblindness + */ + public function set_colorblindness(string $cbn): void { + $this->colorblindness = $cbn; + } + + /** + * Sets user language + * @param string $lang language + * @throws \coding_exception if $lang isn't ISO 639-1 conformant + */ + public function set_lang(string $lang): void { + if (preg_match('/^[a-z]{2}$/', $lang) !== 1) { + throw new \coding_exception('Incorrect language format - must be ISO 639-1, e.g. en or de'); + } + $this->lang = $lang; + } + + /** + * Sets user theme + * @param string $theme theme + */ + public function set_theme(string $theme): void { + $this->theme = $theme; + } + + /** + * sets the associated moodle user (for caching) + * @param \stdClass $mdluser + */ + public function set_mdluser(\stdClass $mdluser): void { + global $USER; + if ($this->mdluser !== null) { + if ($this->mdluser->id !== $USER->id) { + throw new \coding_exception('tried to set cached mdluser twice'); + } + } + $this->mdluser = $mdluser; + } + + /** + * gets the associated moodle user + * @return \stdClass mdluser + */ + public function get_mdluser(): \stdClass { + if ($this->mdluser === null) { + $this->mdluser = user_helper::get_mdluser($this->mdlid); + } + + return $this->mdluser; + } + + /** + * sets the associated plan ID (for caching) + * @param int $planid + */ + public function set_planid(int $planid): void { + if ($this->planid !== null) { + throw new \coding_exception('tried to set cached planid twice'); + } + $this->planid = $planid; + } + + /** + * gets the associated plan ID + * @return int planid + */ + public function get_planid(): int { + if ($this->planid === null) { + $this->planid = plan_helper::get_plan_id($this->mdlid); + } + + return $this->planid; + } + + /** + * gets the associated profile picture + * @return string pfp + */ + public function get_pfp(): string { + if ($this->pfp === null) { + global $PAGE; + $userpicture = new user_picture($this->get_mdluser()); + $userpicture->size = 1; // Size f1. + $this->pfp = $userpicture->get_url($PAGE)->out(false); + } + + return $this->pfp; + } + + /** + * Prepares data for the DB endpoint. + * doesn't set ID if it's 0 + * + * @return object a representation of this course and its data + */ + public function prepare_for_db(): object { + $obj = new \stdClass(); + + $obj->userid = $this->mdlid; + $obj->theme = $this->theme; + $obj->language = $this->lang; + $obj->colorblindness = $this->colorblindness; + $obj->displaytaskcount = $this->displaytaskcount; + + if ($this->lbpid !== 0) { + $obj->id = $this->lbpid; + } + + return $obj; + } + + /** + * Prepares shortened data for the API endpoint. + * + * @return array a shortened representation of this user and its data + */ + public function prepare_for_api_short(): array { + $mdluser = $this->get_mdluser(); + return [ + 'userid' => $this->mdlid, + 'username' => $mdluser->username, + 'firstname' => $mdluser->firstname, + 'lastname' => $mdluser->lastname, + 'profileimageurl' => $this->get_pfp(), + 'vintage' => $mdluser->address, + ]; + } + + /** + * Returns the shortened data structure of a user for the API. + * + * @return external_single_structure The shortened data structure of a user for the API. + */ + public static function api_structure_short(): external_single_structure { + return new external_single_structure( + [ + 'userid' => new external_value(PARAM_INT, 'The id of the user'), + 'username' => new external_value(PARAM_TEXT, 'The username of the user'), + 'firstname' => new external_value(PARAM_TEXT, 'The firstname of the user'), + 'lastname' => new external_value(PARAM_TEXT, 'The lastname of the user'), + 'profileimageurl' => new external_value(PARAM_URL, 'The url of the profile image'), + 'vintage' => new external_value(PARAM_TEXT, 'The vintage of the user'), + ] + ); + } + + /** + * Prepares full data for the API endpoint. + * + * @return array a full representation of this user and its data + */ + public function prepare_for_api(): array { + $mdluser = $this->get_mdluser(); + return [ + 'userid' => $mdluser->id, + 'username' => $mdluser->username, + 'firstname' => $mdluser->firstname, + 'lastname' => $mdluser->lastname, + 'theme' => $this->theme, + 'lang' => $this->lang, + 'profileimageurl' => $this->get_pfp(), + 'planid' => $this->get_planid(), + 'colorblindness' => $this->colorblindness, + 'displaytaskcount' => $this->displaytaskcount, + 'capabilities' => user_helper::get_user_capability_bitmask($this->mdlid), + 'vintage' => $mdluser->address, + ]; + } + + /** + * Returns the full data structure of a user for the API. + * + * @return external_single_structure The full data structure of a user for the API. + */ + public static function api_structure(): external_single_structure { + return new external_single_structure( + [ + 'userid' => new external_value(PARAM_INT, 'The id of the user'), + 'username' => new external_value(PARAM_TEXT, 'The username of the user'), + 'firstname' => new external_value(PARAM_TEXT, 'The firstname of the user'), + 'lastname' => new external_value(PARAM_TEXT, 'The lastname of the user'), + 'theme' => new external_value(PARAM_TEXT, 'The theme the user has selected'), + 'lang' => new external_value(PARAM_TEXT, 'The language the user has selected'), + 'profileimageurl' => new external_value(PARAM_URL, 'The url of the profile image'), + 'planid' => new external_value(PARAM_INT, 'The id of the plan the user is assigned to'), + 'colorblindness' => new external_value(PARAM_TEXT, 'The colorblindness of the user'), + 'displaytaskcount' => new external_value(PARAM_INT, 'If the user has the taskcount-enabled 1-yes 0-no'), + 'capabilities' => new external_value(PARAM_INT, 'The capabilities of the user represented as a bitmask value'), + 'vintage' => new external_value(PARAM_TEXT, 'The vintage of the user'), + ] + ); + } +} diff --git a/lbplanner/classes/polyfill/Enum.php b/lbplanner/classes/polyfill/Enum.php new file mode 100644 index 00000000..410c6273 --- /dev/null +++ b/lbplanner/classes/polyfill/Enum.php @@ -0,0 +1,130 @@ +. + +/** + * polyfill for php8 enums + * + * @package local_lbplanner + * @subpackage polyfill + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\polyfill; + +use ReflectionClass; +use ValueError; +use local_lbplanner\polyfill\EnumCase; + +/** + * Class which is meant to serve as a substitute for native enums. + */ +class Enum { + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @param bool $try whether to return null (true) or throw an error (false) if not found + * @return ?EnumCase the matching enum case or null if not found and $try==true + * @throws ValueError if not found and $try==false + */ + private static function find(mixed $value, bool $try): ?EnumCase { + foreach (static::cases() as $case) { + if ($case->value === $value) { + return $case; + } + } + + if ($try) { + return null; + } else { + throw new ValueError("value {$value} cannot be represented as a value in enum ".static::class); + } + } + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @return mixed either the matching enum value or null if not found + */ + public static function try_from(mixed $value): mixed { + // TODO: replace with nullsafe operator in php8. + $case = static::find($value, true); + if (is_null($case)) { + return null; + } else { + return $case->value; + } + } + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @return mixed the matching enum value + * @throws ValueError if not found + */ + public static function from(mixed $value): mixed { + return static::find($value, false)->value; + } + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @return string the matching enum case name + * @throws mixed either the matching enum case name or null if not found + */ + public static function try_name_from(mixed $value): ?string { + // TODO: replace with nullsafe operator in php8. + $case = static::find($value, true); + if (is_null($case)) { + return null; + } else { + return $case->name; + } + } + /** + * tries to match the passed value to one of the enum values + * @param mixed $value the value to be matched + * @return string the matching enum case name + * @throws ValueError if not found + */ + public static function name_from(mixed $value): string { + return static::find($value, false)->name; + } + /** + * Returns an array of all the cases that exist in this enum + * + * @return EnumCase[] array of cases inside this enum + */ + public static function cases(): array { + $reflection = new ReflectionClass(static::class); + $cases = []; + foreach ($reflection->getConstants() as $name => $val) { + array_push($cases, new EnumCase($name, $val)); + } + return $cases; + } + /** + * Formats all possible enum values into a string + * Example: + * [31=>RED,32=>GREEN,33=>YELLOW] + * @return string the resulting string + */ + public static function format(): string { + $result = "["; + foreach (static::cases() as $case) { + $result .= "{$case->value}=>{$case->name},"; + } + $result[-1] = ']'; + return $result; + } +} diff --git a/lbplanner/classes/polyfill/EnumCase.php b/lbplanner/classes/polyfill/EnumCase.php new file mode 100644 index 00000000..f2451977 --- /dev/null +++ b/lbplanner/classes/polyfill/EnumCase.php @@ -0,0 +1,50 @@ +. + +/** + * case for enums + * + * @package local_lbplanner + * @subpackage polyfill + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace local_lbplanner\polyfill; + +defined('MOODLE_INTERNAL') || die(); + +// TODO: revert to native enums once we migrate to php8. + +/** + * This represents a single case within an Enum + */ +class EnumCase { + /** @var string the name of the case */ + public string $name; + /** @var string the value of the case */ + public $value; + /** + * Constructs an EnumCase + * + * @param string $name the name of the case + * @param mixed $value the value of the case + */ + public function __construct(string $name, $value) { + $this->name = $name; + $this->value = $value; + } +}; diff --git a/lbplanner/db/access.php b/lbplanner/db/access.php index 34b52cb3..3e29ec72 100644 --- a/lbplanner/db/access.php +++ b/lbplanner/db/access.php @@ -14,29 +14,36 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * contains access levels, i.e. capabilities + * + * @package local_lbplanner + * @subpackage db + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + defined('MOODLE_INTERNAL') || die(); -$capabilities = array( - 'local/lb_planner:student' => array( +$capabilities = [ + 'local/lb_planner:student' => [ 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, - ), - 'local/lb_planner:teacher' => array( + ], + 'local/lb_planner:teacher' => [ 'riskbitmask' => RISK_SPAM || RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, - ), - 'local/lb_planner:admin' => array( + ], + 'local/lb_planner:admin' => [ 'riskbitmask' => RISK_SPAM || RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, - ), - 'local/lb_planner:manager' => array( + ], + 'local/lb_planner:manager' => [ 'riskbitmask' => RISK_SPAM || RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, - ) - - -); + ], +]; diff --git a/lbplanner/db/install.php b/lbplanner/db/install.php index 1c3512c8..b6ea5700 100644 --- a/lbplanner/db/install.php +++ b/lbplanner/db/install.php @@ -14,14 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -use local_lbplanner\helpers\user_helper; +/** + * contains some stuff for the first install of the module + * + * @package local_lbplanner + * @subpackage db + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + use local_lbplanner\helpers\config_helper; -defined('MOODLE_INTERNAL') || die; +/** + * Runs when plugin is first installed + */ function xmldb_local_lbplanner_install() { - global $DB; - + config_helper::set_default_active_year(); + config_helper::add_customfield(); } -config_helper::set_default_active_year(); - diff --git a/lbplanner/db/install.xml b/lbplanner/db/install.xml index f0d274fb..86012c1a 100644 --- a/lbplanner/db/install.xml +++ b/lbplanner/db/install.xml @@ -114,5 +114,57 @@ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + +
diff --git a/lbplanner/db/services.php b/lbplanner/db/services.php index b0e6b9a2..3c6bde75 100644 --- a/lbplanner/db/services.php +++ b/lbplanner/db/services.php @@ -14,11 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * contains all service endpoints + * + * @package local_lbplanner + * @subpackage db + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ defined('MOODLE_INTERNAL') || die(); -$functions = array( - 'local_lbplanner_user_get_user' => array( +$functions = [ + 'local_lbplanner_user_get_user' => [ 'classname' => 'local_lbplanner_services\user_get_user', 'methodname' => 'get_user', 'classpath' => 'local/lbplanner/services/user/get_user.php', @@ -26,8 +34,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_user_get_all_users' => array( + ], + 'local_lbplanner_user_get_all_users' => [ 'classname' => 'local_lbplanner_services\user_get_all_users', 'methodname' => 'get_all_users', 'classpath' => 'local/lbplanner/services/user/get_all_users.php', @@ -35,17 +43,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_user_register_user' => array( - 'classname' => 'local_lbplanner_services\user_register_user', - 'methodname' => 'register_user', - 'classpath' => 'local/lbplanner/services/user/register_user.php', - 'description' => 'Register a new user in the lbplanner app', - 'type' => 'write', - 'capabilities' => 'local/lb_planner:student', - 'ajax' => true, - ), - 'local_lbplanner_user_update_user' => array( + ], + 'local_lbplanner_user_update_user' => [ 'classname' => 'local_lbplanner_services\user_update_user', 'methodname' => 'update_user', 'classpath' => 'local/lbplanner/services/user/update_user.php', @@ -53,17 +52,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_courses_get_course' => array( - 'classname' => 'local_lbplanner_services\courses_get_course', - 'methodname' => 'get_course', - 'classpath' => 'local/lbplanner/services/courses/get_course.php', - 'description' => 'Get the data for a course', - 'type' => 'read', - 'capabilities' => 'local/lb_planner:student', - 'ajax' => true, - ), - 'local_lbplanner_courses_get_all_courses' => array( + ], + 'local_lbplanner_courses_get_all_courses' => [ 'classname' => 'local_lbplanner_services\courses_get_all_courses', 'methodname' => 'get_all_courses', 'classpath' => 'local/lbplanner/services/courses/get_all_courses.php', @@ -71,8 +61,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_courses_update_course' => array( + ], + 'local_lbplanner_courses_update_course' => [ 'classname' => 'local_lbplanner_services\courses_update_course', 'methodname' => 'update_course', 'classpath' => 'local/lbplanner/services/courses/update_course.php', @@ -80,8 +70,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_modules_get_module' => array( + ], + 'local_lbplanner_modules_get_module' => [ 'classname' => 'local_lbplanner_services\modules_get_module', 'methodname' => 'get_module', 'classpath' => 'local/lbplanner/services/modules/get_module.php', @@ -89,8 +79,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_modules_get_all_modules' => array( + ], + 'local_lbplanner_modules_get_all_modules' => [ 'classname' => 'local_lbplanner_services\modules_get_all_modules', 'methodname' => 'get_all_modules', 'classpath' => 'local/lbplanner/services/modules/get_all_modules.php', @@ -98,8 +88,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_modules_get_all_course_modules' => array( + ], + 'local_lbplanner_modules_get_all_course_modules' => [ 'classname' => 'local_lbplanner_services\modules_get_all_course_modules', 'methodname' => 'get_all_course_modules', 'classpath' => 'local/lbplanner/services/modules/get_all_course_modules.php', @@ -107,8 +97,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_clear_plan' => array( + ], + 'local_lbplanner_plan_clear_plan' => [ 'classname' => 'local_lbplanner_services\plan_clear_plan', 'methodname' => 'clear_plan', 'classpath' => 'local/lbplanner/services/plan/clear_plan.php', @@ -116,8 +106,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_get_plan' => array( + ], + 'local_lbplanner_plan_get_plan' => [ 'classname' => 'local_lbplanner_services\plan_get_plan', 'methodname' => 'get_plan', 'classpath' => 'local/lbplanner/services/plan/get_plan.php', @@ -125,8 +115,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_invite_user' => array( + ], + 'local_lbplanner_plan_invite_user' => [ 'classname' => 'local_lbplanner_services\plan_invite_user', 'methodname' => 'invite_user', 'classpath' => 'local/lbplanner/services/plan/invite_user.php', @@ -134,8 +124,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_remove_user' => array( + ], + 'local_lbplanner_plan_remove_user' => [ 'classname' => 'local_lbplanner_services\plan_remove_user', 'methodname' => 'remove_user', 'classpath' => 'local/lbplanner/services/plan/remove_user.php', @@ -143,8 +133,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_update_plan' => array( + ], + 'local_lbplanner_plan_update_plan' => [ 'classname' => 'local_lbplanner_services\plan_update_plan', 'methodname' => 'update_plan', 'classpath' => 'local/lbplanner/services/plan/update_plan.php', @@ -152,8 +142,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_leave_plan' => array( + ], + 'local_lbplanner_plan_leave_plan' => [ 'classname' => 'local_lbplanner_services\plan_leave_plan', 'methodname' => 'leave_plan', 'classpath' => 'local/lbplanner/services/plan/leave_plan.php', @@ -161,17 +151,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_add_deadline' => array( - 'classname' => 'local_lbplanner_services\plan_add_deadline', - 'methodname' => 'add_deadline', - 'classpath' => 'local/lbplanner/services/plan/add_deadline.php', - 'description' => 'Add a deadline to the plan', - 'type' => 'write', - 'capabilities' => 'local/lb_planner:student', - 'ajax' => true, - ), - 'local_lbplanner_plan_delete_deadline' => array( + ], + 'local_lbplanner_plan_delete_deadline' => [ 'classname' => 'local_lbplanner_services\plan_delete_deadline', 'methodname' => 'delete_deadline', 'classpath' => 'local/lbplanner/services/plan/delete_deadline.php', @@ -179,8 +160,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_user_delete_user' => array( + ], + 'local_lbplanner_user_delete_user' => [ 'classname' => 'local_lbplanner_services\user_delete_user', 'methodname' => 'delete_user', 'classpath' => 'local/lbplanner/services/user/delete_user.php', @@ -188,26 +169,17 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_update_deadline' => array( - 'classname' => 'local_lbplanner_services\plan_update_deadline', - 'methodname' => 'update_deadline', - 'classpath' => 'local/lbplanner/services/plan/update_deadline.php', - 'description' => 'Update a deadline from the plan', + ], + 'local_lbplanner_plan_set_deadline' => [ + 'classname' => 'local_lbplanner_services\plan_set_deadline', + 'methodname' => 'set_deadline', + 'classpath' => 'local/lbplanner/services/plan/set_deadline.php', + 'description' => 'Set a deadline from the plan', 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_get_access' => array( - 'classname' => 'local_lbplanner_services\plan_get_access', - 'methodname' => 'get_access', - 'classpath' => 'local/lbplanner/services/plan/get_access.php', - 'description' => 'Get the access type to the plan', - 'type' => 'read', - 'capabilities' => 'local/lb_planner:student', - 'ajax' => true, - ), - 'local_lbplanner_plan_update_access' => array( + ], + 'local_lbplanner_plan_update_access' => [ 'classname' => 'local_lbplanner_services\plan_update_access', 'methodname' => 'update_access', 'classpath' => 'local/lbplanner/services/plan/update_access.php', @@ -215,8 +187,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_get_invites' => array( + ], + 'local_lbplanner_plan_get_invites' => [ 'classname' => 'local_lbplanner_services\plan_get_invites', 'methodname' => 'get_invites', 'classpath' => 'local/lbplanner/services/plan/get_invites.php', @@ -224,8 +196,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_notifications_get_all_notifications' => array( + ], + 'local_lbplanner_notifications_get_all_notifications' => [ 'classname' => 'local_lbplanner_services\notifications_get_all_notifications', 'methodname' => 'get_all_notifications', 'classpath' => 'local/lbplanner/services/notifications/get_all_notifications.php', @@ -233,8 +205,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_notifications_update_notification' => array( + ], + 'local_lbplanner_notifications_update_notification' => [ 'classname' => 'local_lbplanner_services\notifications_update_notification', 'methodname' => 'update_notification', 'classpath' => 'local/lbplanner/services/notifications/update_notification.php', @@ -242,8 +214,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_feedback_submit_feedback' => array( + ], + 'local_lbplanner_feedback_submit_feedback' => [ 'classname' => 'local_lbplanner_services\feedback_submit_feedback', 'methodname' => 'submit_feedback', 'classpath' => 'local/lbplanner/services/feedback/submit_feedback.php', @@ -251,17 +223,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_feedback_get_feedback' => array( - 'classname' => 'local_lbplanner_services\feedback_get_feedback', - 'methodname' => 'get_feedback', - 'classpath' => 'local/lbplanner/services/feedback/get_feedback.php', - 'description' => 'Get the feedback of the given id', - 'type' => 'read', - 'capabilities' => 'local/lb_planner:admin, local/lb_planner:manager', - 'ajax' => true, - ), - 'local_lbplanner_feedback_get_all_feedbacks' => array( + ], + 'local_lbplanner_feedback_get_all_feedbacks' => [ 'classname' => 'local_lbplanner_services\feedback_get_all_feedbacks', 'methodname' => 'get_all_feedbacks', 'classpath' => 'local/lbplanner/services/feedback/get_all_feedbacks.php', @@ -269,8 +232,8 @@ 'type' => 'read', 'capabilities' => 'local/lb_planner:admin, local/lb_planner:manager', 'ajax' => true, - ), - 'local_lbplanner_feedback_update_feedback' => array( + ], + 'local_lbplanner_feedback_update_feedback' => [ 'classname' => 'local_lbplanner_services\feedback_update_feedback', 'methodname' => 'update_feedback', 'classpath' => 'local/lbplanner/services/feedback/update_feedback.php', @@ -278,8 +241,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:admin, local/lb_planner:manager', 'ajax' => true, - ), - 'local_lbplanner_feedback_delete_feedback' => array( + ], + 'local_lbplanner_feedback_delete_feedback' => [ 'classname' => 'local_lbplanner_services\feedback_delete_feedback', 'methodname' => 'delete_feedback', 'classpath' => 'local/lbplanner/services/feedback/delete_feedback.php', @@ -287,8 +250,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:admin, local/lb_planner:manager', 'ajax' => true, - ), - 'local_lbplanner_plan_accept_invite' => array( + ], + 'local_lbplanner_plan_accept_invite' => [ 'classname' => 'local_lbplanner_services\plan_accept_invite', 'methodname' => 'accept_invite', 'classpath' => 'local/lbplanner/services/plan/accept_invite.php', @@ -296,8 +259,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_plan_decline_invite' => array( + ], + 'local_lbplanner_plan_decline_invite' => [ 'classname' => 'local_lbplanner_services\plan_decline_invite', 'methodname' => 'decline_invite', 'classpath' => 'local/lbplanner/services/plan/decline_invite.php', @@ -305,8 +268,8 @@ 'type' => 'write', 'capabilities' => 'local/lb_planner:student', 'ajax' => true, - ), - 'local_lbplanner_config_get_version' => array( + ], + 'local_lbplanner_config_get_version' => [ 'classname' => 'local_lbplanner_services\config_get_version', 'methodname' => 'get_version', 'classpath' => 'local/lbplanner/services/config/get_version.php', @@ -314,23 +277,74 @@ 'type' => 'read', 'capabilities' => '', 'ajax' => true, - ), -); + ], + 'local_lbplanner_slots_get_my_slots' => [ + 'classname' => 'local_lbplanner_services\slots_get_my_slots', + 'methodname' => 'get_my_slots', + 'classpath' => 'local/lbplanner/services/slots/get_my_slots.php', + 'description' => 'Get all slots the user can theoretically reserve.', + 'type' => 'read', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_get_student_slots' => [ + 'classname' => 'local_lbplanner_services\slots_get_student_slots', + 'methodname' => 'get_student_slots', + 'classpath' => 'local/lbplanner/services/slots/get_student_slots.php', + 'description' => 'Get all slots a supervisor can theoretically reserve for a student.', + 'type' => 'read', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_get_supervisor_slots' => [ + 'classname' => 'local_lbplanner_services\slots_get_supervisor_slots', + 'methodname' => 'get_supervisor_slots', + 'classpath' => 'local/lbplanner/services/slots/get_supervisor_slots.php', + 'description' => 'Get all slots belonging to the supervisor.', + 'type' => 'read', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_book_reservation' => [ + 'classname' => 'local_lbplanner_services\slots_book_reservation', + 'methodname' => 'book_reservation', + 'classpath' => 'local/lbplanner/services/slots/book_reservation.php', + 'description' => 'Book a reservation', + 'type' => 'write', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_unbook_reservation' => [ + 'classname' => 'local_lbplanner_services\slots_unbook_reservation', + 'methodname' => 'unbook_reservation', + 'classpath' => 'local/lbplanner/services/slots/unbook_reservation.php', + 'description' => 'Unbook a reservation', + 'type' => 'write', + 'capabilities' => 'local/lb_planner:student', + 'ajax' => true, + ], + 'local_lbplanner_slots_create_slot' => [ + 'classname' => 'local_lbplanner_services\slots_create_slot', + 'methodname' => 'create_slot', + 'classpath' => 'local/lbplanner/services/slots/create_slot.php', + 'description' => 'Create a slot', + 'type' => 'write', + 'capabilities' => 'local/lb_planner:teacher', + 'ajax' => true, + ], +]; -$services = array( - 'LB Planner API' => array( - 'functions' => array( +$services = [ + 'LB Planner API' => [ + 'functions' => [ 'local_lbplanner_user_get_user', 'local_lbplanner_user_get_all_users', - 'local_lbplanner_user_register_user', 'local_lbplanner_user_update_user', 'local_lbplanner_courses_get_all_courses', - 'local_lbplanner_courses_get_course', 'local_lbplanner_courses_update_course', 'local_lbplanner_modules_get_all_course_modules', 'local_lbplanner_modules_get_all_modules', 'local_lbplanner_modules_get_module', - 'local_lbplanner_plan_add_deadline', 'local_lbplanner_plan_clear_plan', 'local_lbplanner_plan_delete_deadline', 'local_lbplanner_plan_get_plan', @@ -338,7 +352,7 @@ 'local_lbplanner_plan_get_invites', 'local_lbplanner_plan_leave_plan', 'local_lbplanner_plan_remove_user', - 'local_lbplanner_plan_update_deadline', + 'local_lbplanner_plan_set_deadline', 'local_lbplanner_plan_update_plan', 'local_lbplanner_notifications_get_all_notifications', 'local_lbplanner_notifications_update_notification', @@ -348,14 +362,18 @@ 'local_lbplanner_feedback_submit_feedback', 'local_lbplanner_feedback_delete_feedback', 'local_lbplanner_feedback_get_all_feedbacks', - 'local_lbplanner_feedback_get_feedback', 'local_lbplanner_feedback_update_feedback', 'local_lbplanner_plan_accept_invite', 'local_lbplanner_plan_decline_invite', 'local_lbplanner_config_get_version', - ), + 'local_lbplanner_slots_book_reservation', + 'local_lbplanner_slots_create_slot', + 'local_lbplanner_slots_get_my_slots', + 'local_lbplanner_slots_get_student_slots', + 'local_lbplanner_slots_get_supervisor_slots', + ], 'restrictedusers' => 0, 'enabled' => 1, - 'shortname' => 'lb_planner_api' - ), -); + 'shortname' => 'lb_planner_api', + ], +]; diff --git a/lbplanner/db/upgrade.php b/lbplanner/db/upgrade.php index bd52dcd0..feee43e2 100644 --- a/lbplanner/db/upgrade.php +++ b/lbplanner/db/upgrade.php @@ -14,10 +14,29 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * for upgrading the db + * + * @package local_lbplanner + * @subpackage db + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + use local_lbplanner\helpers\config_helper; -function xmldb_local_lbplanner_upgrade($oldversion) { - config_helper::set_default_active_year(); +/** + * Upgrades the DB version + * right now it only sets the default active year + * + * @param mixed $oldversion (unused) the previous version to upgrade from + * @return bool true + */ +function xmldb_local_lbplanner_upgrade($oldversion): bool { + if ($oldversion < 2024022700) { + config_helper::set_default_active_year(); + config_helper::add_customfield(); + } return true; } diff --git a/lbplanner/lang/en/local_lbplanner.php b/lbplanner/lang/en/local_lbplanner.php index 85f45bbd..71a353f1 100644 --- a/lbplanner/lang/en/local_lbplanner.php +++ b/lbplanner/lang/en/local_lbplanner.php @@ -14,7 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -defined('MOODLE_INTERNAL') or die(); +/** + * Defines some translation strings in english + * + * @package local_lbplanner + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); $string['pluginname'] = 'LB Planner'; $string['lb_planner:student'] = 'LB Planner Student'; diff --git a/lbplanner/services/config/get_version.php b/lbplanner/services/config/get_version.php index 3f3c51ec..1f66b731 100644 --- a/lbplanner/services/config/get_version.php +++ b/lbplanner/services/config/get_version.php @@ -23,25 +23,43 @@ /** * Get version service. + * + * @package local_lbplanner + * @subpackage services_config + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class config_get_version extends external_api { - public static function get_version_parameters() { + /** + * Parameters for get_version. + * @return external_function_parameters + */ + public static function get_version_parameters(): external_function_parameters { return new external_function_parameters( - array() + [] ); } - public static function get_version() { - $release = get_config('local_lbplanner', 'release'); - return array( - 'release' => get_config('local_lbplanner', 'release')); + /** + * Returns the version. + * + * @return array containing the version + */ + public static function get_version(): array { + return [ + 'release' => get_config('local_lbplanner', 'release'), + ]; } - public static function get_version_returns() { + /** + * Returns the structure of the versioning array. + * @return external_single_structure + */ + public static function get_version_returns(): external_single_structure { return new external_single_structure( - array( + [ 'release' => new external_value(PARAM_TEXT, 'the current LBPlanner version'), - ) + ] ); } } diff --git a/lbplanner/services/courses/get_all_courses.php b/lbplanner/services/courses/get_all_courses.php index d50fcf58..2ee6374e 100644 --- a/lbplanner/services/courses/get_all_courses.php +++ b/lbplanner/services/courses/get_all_courses.php @@ -16,105 +16,50 @@ namespace local_lbplanner_services; -use coding_exception; -use dml_exception; use external_api; use external_function_parameters; use external_multiple_structure; -use external_single_structure; -use external_value; -use invalid_parameter_exception; -use local_lbplanner\helpers\user_helper; + use local_lbplanner\helpers\course_helper; -use moodle_exception; +use local_lbplanner\model\course; /** * Get all the courses of the current year. + * + * @package local_lbplanner + * @subpackage services_courses + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class courses_get_all_courses extends external_api { + + /** + * Has no Parameters + * @return external_function_parameters + */ public static function get_all_courses_parameters(): external_function_parameters { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the courses for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); + return new external_function_parameters([]); } /** - * @throws coding_exception - * @throws dml_exception - * @throws moodle_exception - * @throws invalid_parameter_exception + * Get all the courses of the current year. */ - public static function get_all_courses($userid): array { - global $DB; - - self::validate_parameters(self::get_all_courses_parameters(), array('userid' => $userid)); - - user_helper::assert_access($userid); - - $courses = enrol_get_my_courses(); - // Remove Duplicates. - $courses = array_unique($courses, SORT_REGULAR); - - // Check this out: https://www.youtube.com/watch?v=z3Pzfi476HI . - $catgirls = array(); - + public static function get_all_courses(): array { + $courses = course_helper::get_all_lbplanner_courses(); + $results = []; foreach ($courses as $course) { - $courseid = $course->id; - $name = $course->fullname; - $shortname = substr($course->shortname, 0, 5); - - if (strpos($shortname, ' ') !== false) { - $shortname = substr($shortname, 0, strpos($shortname, ' ')); - } - - if (!course_helper::check_current_year($courseid)) { - continue; - } - if ($DB->record_exists( - course_helper::LBPLANNER_COURSE_TABLE, array('courseid' => $courseid, 'userid' => $userid) - )) { - $fetchedcourse = $DB->get_record( - course_helper::LBPLANNER_COURSE_TABLE, array('courseid' => $courseid, 'userid' => $userid), - '*', - MUST_EXIST - ); - $fetchedcourse->name = $name; - $catgirls[] = (object) $fetchedcourse; - } else { - $catgirl = (object) array( - 'courseid' => $courseid, - 'color' => course_helper::COLORS[array_rand(course_helper::COLORS)], - 'shortname' => strtoupper($shortname), - 'enabled' => course_helper::DISABLED_COURSE, - 'userid' => $userid, - ); - $DB->insert_record(course_helper::LBPLANNER_COURSE_TABLE, $catgirl); - $catgirl->name = $name; - $catgirls[] = $catgirl; - } - + array_push($results, $course->prepare_for_api()); } - return $catgirls; + return $results; } + /** + * Returns description of method result value + * @return external_multiple_structure description of method result value + */ public static function get_all_courses_returns(): external_multiple_structure { return new external_multiple_structure( - new external_single_structure( - array( - 'courseid' => new external_value(PARAM_INT, 'The id of the course'), - 'color' => new external_value(PARAM_TEXT, 'The color of the course'), - 'name' => new external_value(PARAM_TEXT, 'The name of the course'), - 'shortname' => new external_value(PARAM_TEXT, 'The shortname of the course'), - 'enabled' => new external_value(PARAM_BOOL, 'Whether the course is enabled or not'), - 'userid' => new external_value(PARAM_INT, 'The id of the user'), - ) - ) + course::api_structure() ); } } diff --git a/lbplanner/services/courses/get_course.php b/lbplanner/services/courses/get_course.php deleted file mode 100644 index e59d98fc..00000000 --- a/lbplanner/services/courses/get_course.php +++ /dev/null @@ -1,74 +0,0 @@ -. - -namespace local_lbplanner_services; - -use external_api; -use external_function_parameters; -use external_single_structure; -use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\course_helper; - -/** - * Get the data for a course. - */ -class courses_get_course extends external_api { - public static function get_course_parameters() { - return new external_function_parameters(array( - 'courseid' => new external_value(PARAM_INT, 'The id of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); - } - - public static function get_course($courseid, $userid) { - global $DB; - - self::validate_parameters(self::get_course_parameters(), array('courseid' => $courseid, 'userid' => $userid)); - - if (!$DB->record_exists('course', array('id' => $courseid))) { - throw new \moodle_exception('Course not found'); - } - - user_helper::assert_access($userid); - - if (!course_helper::check_access($courseid, $userid)) { - throw new \moodle_exception('Not Enrolled in course'); - } - - $course = $DB->get_record( - course_helper::LBPLANNER_COURSE_TABLE, - array('courseid' => $courseid, 'userid' => $userid), - '*', - MUST_EXIST - ); - $course->name = course_helper::get_fullname($course->courseid); - return $course; - } - - public static function get_course_returns() { - return new external_single_structure( - array( - 'courseid' => new external_value(PARAM_INT, 'The id of the course'), - 'color' => new external_value(PARAM_TEXT, 'The color of the course'), - 'name' => new external_value(PARAM_TEXT, 'The name of the course'), - 'shortname' => new external_value(PARAM_TEXT, 'The shortname of the course'), - 'enabled' => new external_value(PARAM_BOOL, 'Whether the course is enabled or not'), - 'userid' => new external_value(PARAM_INT, 'The id of the user'), - ) - ); - } -} diff --git a/lbplanner/services/courses/update_course.php b/lbplanner/services/courses/update_course.php index 21c2dcf2..26ed6a87 100644 --- a/lbplanner/services/courses/update_course.php +++ b/lbplanner/services/courses/update_course.php @@ -16,77 +16,87 @@ namespace local_lbplanner_services; +use dml_exception; use external_api; use external_function_parameters; -use external_single_structure; use external_value; -use local_lbplanner\helpers\user_helper; +use invalid_parameter_exception; use local_lbplanner\helpers\course_helper; +use moodle_exception; /** * Update the data for a course. + * + * @package local_lbplanner + * @subpackage services_courses + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class courses_update_course extends external_api { - public static function update_course_parameters() { - return new external_function_parameters(array( + /** + * Parameters for update_course. + * @return external_function_parameters + */ + public static function update_course_parameters(): external_function_parameters { + return new external_function_parameters([ 'courseid' => new external_value(PARAM_INT, 'The id of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'color' => new external_value(PARAM_TEXT, 'The color of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'shortname' => new external_value(PARAM_TEXT, 'The shortname of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + 'color' => new external_value(PARAM_TEXT, 'The color of the course in HEX', VALUE_DEFAULT, null), + 'shortname' => new external_value(PARAM_TEXT, 'The shortname of the course', VALUE_DEFAULT, null), 'enabled' => new external_value( PARAM_BOOL, 'Whether the course is enabled or not', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED + VALUE_DEFAULT, + null ), - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + ]); } - public static function update_course($courseid, $color, $shortname, $enabled, $userid) { - global $DB; + /** + * Update the User-data for a course. + * @param int $courseid The id of the course + * @param string $color The color of the course + * @param string $shortname The shortname of the course + * @param int $enabled Whether the course is enabled or not (0 or 1) + * @return void + * @throws moodle_exception + */ + public static function update_course(int $courseid, string $color, string $shortname, int $enabled): void { + global $DB, $USER; self::validate_parameters( self::update_course_parameters(), - array( + [ 'courseid' => $courseid, 'color' => $color, 'shortname' => $shortname, 'enabled' => $enabled, - 'userid' => $userid - ) + ] ); - user_helper::assert_access($userid); - - if (!course_helper::check_access($courseid, $userid)) { - throw new \moodle_exception('Access denied'); - } - $course = course_helper::get_lbplanner_course($courseid, $userid); - if (strlen($shortname) > 5) { - throw new \moodle_exception('Shortname is too long'); + throw new moodle_exception('Shortname is too long'); } - $course->color = $color; - $course->shortname = $shortname; - $course->enabled = $enabled; - $DB->update_record(course_helper::LBPLANNER_COURSE_TABLE, $course); - $course->name = course_helper::get_fullname($course->courseid); + $course = course_helper::get_lbplanner_course($courseid, $USER->id); + + if ($color !== null) { + $course->set_color($color); + } + if ($shortname !== null) { + $course->set_shortname($shortname); + } + if ($enabled !== null) { + $course->set_enabled((bool) $enabled); + } - return $course; + $DB->update_record(course_helper::LBPLANNER_COURSE_TABLE, $course->prepare_for_db()); } + /** + * Returns nothing. + * @return null + */ public static function update_course_returns() { - return new external_single_structure( - array( - 'courseid' => new external_value(PARAM_INT, 'The id of the course'), - 'color' => new external_value(PARAM_TEXT, 'The color of the course'), - 'name' => new external_value(PARAM_TEXT, 'The name of the course'), - 'shortname' => new external_value(PARAM_TEXT, 'The shortname of the course'), - 'enabled' => new external_value(PARAM_BOOL, 'Whether the course is enabled or not'), - 'userid' => new external_value(PARAM_INT, 'The id of the user'), - ) - ); + return null; } } diff --git a/lbplanner/services/feedback/delete_feedback.php b/lbplanner/services/feedback/delete_feedback.php index a75455fb..6b5cefb9 100644 --- a/lbplanner/services/feedback/delete_feedback.php +++ b/lbplanner/services/feedback/delete_feedback.php @@ -20,44 +20,56 @@ use external_multiple_structure; use external_function_parameters; use external_value; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\feedback_helper; /** * Deletes feedback from the database. + * + * @package local_lbplanner + * @subpackage services_feedback + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class feedback_delete_feedback extends external_api { - public static function delete_feedback_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'feedbackid' => new external_value(PARAM_INT, 'The id of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + /** + * Parameters for delete_feedback. + * @return external_function_parameters + */ + public static function delete_feedback_parameters(): external_function_parameters { + return new external_function_parameters([ + 'feedbackid' => new external_value(PARAM_INT, 'ID of the feedback to delete', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); } - public static function delete_feedback($userid, $feedbackid) { - global $DB; + /** + * Deletes feedback from the database. + * + * @param int $feedbackid ID of the feedback to delete + * @return void + * @throws \moodle_exception when feedback wasn't found + */ + public static function delete_feedback(int $feedbackid) { + global $DB, $USER; self::validate_parameters( self::delete_feedback_parameters(), - array('userid' => $userid , 'feedbackid' => $feedbackid) + ['feedbackid' => $feedbackid] ); - user_helper::assert_access($userid); - - if (!$DB->record_exists(feedback_helper::LBPLANNER_FEEDBACK_TABLE, array('id' => $feedbackid))) { + if (!$DB->record_exists(feedback_helper::LBPLANNER_FEEDBACK_TABLE, ['id' => $feedbackid])) { throw new \moodle_exception('feedback_not_found'); } feedback_helper::assert_admin_access(); - $DB->delete_records(feedback_helper::LBPLANNER_FEEDBACK_TABLE, array('id' => $feedbackid)); - - return feedback_helper::get_all_feedbacks($userid); + $DB->delete_records(feedback_helper::LBPLANNER_FEEDBACK_TABLE, ['id' => $feedbackid]); } + /** + * Returns the structure of nothing. + * @return null + */ public static function delete_feedback_returns() { - return new external_multiple_structure( - feedback_helper::structure(), - ); + return null; } } diff --git a/lbplanner/services/feedback/get_all_feedbacks.php b/lbplanner/services/feedback/get_all_feedbacks.php index d33385a1..207ee2ef 100644 --- a/lbplanner/services/feedback/get_all_feedbacks.php +++ b/lbplanner/services/feedback/get_all_feedbacks.php @@ -19,36 +19,40 @@ use external_api; use external_function_parameters; use external_multiple_structure; -use external_value; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\feedback_helper; /** * Get all feedback from the database. + * + * @package local_lbplanner + * @subpackage services_feedback + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class feedback_get_all_feedbacks extends external_api { - public static function get_all_feedbacks_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + /** + * Parameters for get_all_feedbacks. + * @return external_function_parameters + */ + public static function get_all_feedbacks_parameters(): external_function_parameters { + return new external_function_parameters([]); } - public static function get_all_feedbacks($userid) { - global $DB; - - self::validate_parameters( - self::get_all_feedbacks_parameters(), - array('userid' => $userid) - ); - - user_helper::assert_access($userid); - + /** + * Returns all feedbacks from the database. + * + * @return array all feedback objects + */ + public static function get_all_feedbacks(): array { feedback_helper::assert_admin_access(); - - return feedback_helper::get_all_feedbacks($userid); + return feedback_helper::get_all_feedbacks(); } - public static function get_all_feedbacks_returns() { + /** + * Returns the structure of the array of feedbacks. + * @return external_multiple_structure + */ + public static function get_all_feedbacks_returns(): external_multiple_structure { return new external_multiple_structure( feedback_helper::structure(), ); diff --git a/lbplanner/services/feedback/get_feedback.php b/lbplanner/services/feedback/get_feedback.php deleted file mode 100644 index a280630e..00000000 --- a/lbplanner/services/feedback/get_feedback.php +++ /dev/null @@ -1,57 +0,0 @@ -. - -namespace local_lbplanner_services; - -use external_api; -use external_function_parameters; -use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\feedback_helper; - -/** - * Get feedback from the database. - */ -class feedback_get_feedback extends external_api { - public static function get_feedback_parameters() { - return new external_function_parameters(array( - 'feedbackid' => new external_value(PARAM_INT, 'The id of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); - } - - public static function get_feedback($feedbackid, $userid) { - global $DB; - - self::validate_parameters( - self::get_feedback_parameters(), - array('feedbackid' => $feedbackid, 'userid' => $userid) - ); - - user_helper::assert_access($userid); - feedback_helper::assert_admin_access(); - - if (!$DB->record_exists(feedback_helper::LBPLANNER_FEEDBACK_TABLE, array('id' => $feedbackid))) { - throw new \moodle_exception('feedback_not_found'); - } - - return feedback_helper::get_feedback($feedbackid); - } - - public static function get_feedback_returns() { - return feedback_helper::structure(); - } -} diff --git a/lbplanner/services/feedback/submit_feedback.php b/lbplanner/services/feedback/submit_feedback.php index 15722cb9..1c857c67 100644 --- a/lbplanner/services/feedback/submit_feedback.php +++ b/lbplanner/services/feedback/submit_feedback.php @@ -18,49 +18,86 @@ use external_api; use external_function_parameters; -use external_single_structure; use external_value; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\feedback_helper; /** * Add feedback to the database. + * + * @package local_lbplanner + * @subpackage services_feedback + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class feedback_submit_feedback extends external_api { - public static function submit_feedback_parameters() { + /** + * Parameters for submit_feedback. + * @return external_function_parameters + */ + public static function submit_feedback_parameters(): external_function_parameters { return new external_function_parameters( - array( - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'type' => new external_value(PARAM_INT, 'The type ', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'content' => new external_value(PARAM_TEXT, 'The content of the feedback', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'logfile' => new external_value(PARAM_TEXT, 'The name of the logfile', VALUE_DEFAULT, null, NULL_NOT_ALLOWED ), - ) + [ + 'type' => new external_value( + PARAM_INT, + 'type of Feedback (bug, typo, feature, other)', // TODO: use enums. + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED, + ), + 'content' => new external_value( + PARAM_TEXT, + 'feedback contents', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED, + ), + 'logfile' => new external_value( + PARAM_TEXT, + 'file name of the associated log file', + VALUE_DEFAULT, + null, + NULL_NOT_ALLOWED, + ), + ] ); } - public static function submit_feedback($userid, $type, $content, $logfile) { - global $DB; + /** + * Add feedback to the database. + * + * @param int $type type of Feedback + * @see feedback_helper + * @param string $content feedback contents + * @param string $logfile file name of the associated log file + * @return int The ID of the new feedback + */ + public static function submit_feedback(int $type, string $content, string $logfile): int { + global $DB, $USER; self::validate_parameters( self::submit_feedback_parameters(), - array('userid' => $userid, 'type' => $type, 'content' => $content, 'logfile' => $logfile) + ['type' => $type, 'content' => $content, 'logfile' => $logfile] ); - user_helper::assert_access($userid); + // TODO: validate $type. - $id = $DB->insert_record(feedback_helper::LBPLANNER_FEEDBACK_TABLE, array( + $id = $DB->insert_record(feedback_helper::LBPLANNER_FEEDBACK_TABLE, [ 'content' => $content, - 'userid' => $userid, + 'userid' => $USER->id, 'type' => $type, 'status' => feedback_helper::STATUS_UNREAD, 'timestamp' => time(), 'logfile' => $logfile, - )); + ]); - return feedback_helper::get_feedback($id); + return $id; } - public static function submit_feedback_returns() { - return feedback_helper::structure(); + /** + * Returns the structure of the feedback ID. + * @return external_value + */ + public static function submit_feedback_returns(): external_value { + return new external_value(PARAM_INT, "The ID of the new feedback"); } } diff --git a/lbplanner/services/feedback/update_feedback.php b/lbplanner/services/feedback/update_feedback.php index 256e217d..85df8503 100644 --- a/lbplanner/services/feedback/update_feedback.php +++ b/lbplanner/services/feedback/update_feedback.php @@ -20,52 +20,71 @@ use external_function_parameters; use external_value; use gradereport_singleview\local\ui\feedback; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\feedback_helper; /** * Updates feedback from the database. + * + * @package local_lbplanner + * @subpackage services_feedback + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class feedback_update_feedback extends external_api { - public static function update_feedback_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'feedbackid' => new external_value(PARAM_INT, 'The id of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'notes' => new external_value(PARAM_TEXT, 'The notes of the feedback', VALUE_DEFAULT, null, NULL_ALLOWED), - 'status' => new external_value(PARAM_INT, 'The status of the feedback', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + /** + * Parameters for update_feedback. + * @return external_function_parameters + */ + public static function update_feedback_parameters(): external_function_parameters { + return new external_function_parameters([ + 'feedbackid' => + new external_value(PARAM_INT, 'ID of the feedback to be updated', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + 'notes' => new external_value(PARAM_TEXT, 'updated notes', VALUE_DEFAULT, null, NULL_ALLOWED), + 'status' => new external_value(PARAM_INT, 'updated status', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); } - public static function update_feedback($userid, $feedbackid, $notes, $status) { - global $DB; + /** + * Updates feedback from the database. + * + * @param int $feedbackid ID of the feedback to be updated + * @param string $notes updated notes + * @param int $status updated status + * @see feedback_helper + * @return void + * @throws \moodle_exception when feedback not found or status invalid + */ + public static function update_feedback(int $feedbackid, string $notes, int $status) { + global $DB, $USER; self::validate_parameters( self::update_feedback_parameters(), - array('userid' => $userid , 'feedbackid' => $feedbackid, 'notes' => $notes, 'status' => $status) + ['feedbackid' => $feedbackid, 'notes' => $notes, 'status' => $status] ); - user_helper::assert_access($userid); feedback_helper::assert_admin_access(); - if (!$DB->record_exists(feedback_helper::LBPLANNER_FEEDBACK_TABLE, array('id' => $feedbackid))) { + if (!$DB->record_exists(feedback_helper::LBPLANNER_FEEDBACK_TABLE, ['id' => $feedbackid])) { throw new \moodle_exception('feedback_not_found'); } - $feedback = $DB->get_record(feedback_helper::LBPLANNER_FEEDBACK_TABLE, array('id' => $feedbackid), '*', MUST_EXIST); + $feedback = $DB->get_record(feedback_helper::LBPLANNER_FEEDBACK_TABLE, ['id' => $feedbackid], '*', MUST_EXIST); $feedback->notes = $notes; - if ($status > 1 || $status < 0) { + if ($status > 1 || $status < 0) { // TODO: use enum to validate. throw new \moodle_exception('Invalid status'); } $feedback->status = $status; $feedback->lastmodified = time(); - $feedback->lastmodifiedby = $userid; + $feedback->lastmodifiedby = $USER->id; $DB->update_record(feedback_helper::LBPLANNER_FEEDBACK_TABLE, $feedback); - - return $feedback; } + /** + * Returns the structure of nothing. + * @return null + */ public static function update_feedback_returns() { - return feedback_helper::structure(); + return null; } } diff --git a/lbplanner/services/modules/get_all_course_modules.php b/lbplanner/services/modules/get_all_course_modules.php index 9ca6555b..7222f68a 100644 --- a/lbplanner/services/modules/get_all_course_modules.php +++ b/lbplanner/services/modules/get_all_course_modules.php @@ -21,39 +21,55 @@ use external_multiple_structure; use external_value; use local_lbplanner\helpers\modules_helper; -use local_lbplanner\helpers\user_helper; /** * Get all the modules of the given course. + * + * @package local_lbplanner + * @subpackage services_modules + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class modules_get_all_course_modules extends external_api { - public static function get_all_course_modules_parameters() { - return new external_function_parameters(array( + /** + * Parameters for get_all_course_modules + * @return external_function_parameters + */ + public static function get_all_course_modules_parameters(): external_function_parameters { + return new external_function_parameters([ 'courseid' => new external_value(PARAM_INT, 'The id of the course', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), 'ekenabled' => new external_value( - PARAM_BOOL, - 'Whether or not to include ek modules', - VALUE_REQUIRED, - false, - NULL_NOT_ALLOWED), - )); + PARAM_BOOL, + 'Whether to include ek modules', + VALUE_DEFAULT, + false, + NULL_NOT_ALLOWED + ), + ]); } - public static function get_all_course_modules($courseid, $userid, $ekenabled) { - global $DB; - + /** + * Returns all the modules inside a course. + * + * @param int $courseid The ID of the course + * @param bool $ekenabled whether or not to include ek modules + * @return array the modules + */ + public static function get_all_course_modules(int $courseid, bool $ekenabled): array { + global $USER; self::validate_parameters( self::get_all_course_modules_parameters(), - array('courseid' => $courseid, 'userid' => $userid, 'ekenabled' => $ekenabled) + ['courseid' => $courseid, 'ekenabled' => $ekenabled] ); - user_helper::assert_access($userid); - - return modules_helper::get_all_course_modules($courseid, $userid, $ekenabled); + return modules_helper::get_all_course_modules($courseid, $USER->id, $ekenabled); } - public static function get_all_course_modules_returns() { + /** + * Returns the structure of the module array. + * @return external_multiple_structure + */ + public static function get_all_course_modules_returns(): external_multiple_structure { return new external_multiple_structure( modules_helper::structure(), ); diff --git a/lbplanner/services/modules/get_all_modules.php b/lbplanner/services/modules/get_all_modules.php index 6d2d08e3..67ee3932 100644 --- a/lbplanner/services/modules/get_all_modules.php +++ b/lbplanner/services/modules/get_all_modules.php @@ -19,51 +19,61 @@ use external_api; use external_function_parameters; use external_multiple_structure; -use external_value; use local_lbplanner\helpers\course_helper; use local_lbplanner\helpers\modules_helper; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\plan_helper; /** * Get all the modules of the current year. + * + * @package local_lbplanner + * @subpackage services_modules + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class modules_get_all_modules extends external_api { - public static function get_all_modules_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + /** + * Parameters for get_all_modules. + * @return external_function_parameters + */ + public static function get_all_modules_parameters(): external_function_parameters { + return new external_function_parameters([]); } - public static function get_all_modules($userid) { - global $DB; + /** + * Returns all the modules for a user. + * + * @return array the modules + */ + public static function get_all_modules(): array { + global $USER; - self::validate_parameters(self::get_all_modules_parameters(), array('userid' => $userid)); + $modules = []; - user_helper::assert_access($userid); - - $modules = array(); - - $courses = self::call_external_function('local_lbplanner_courses_get_all_courses', array('userid' => $userid)); - $plan = plan_helper::get_plan(plan_helper::get_plan_id($userid)); + $courses = course_helper::get_all_lbplanner_courses(); + $plan = plan_helper::get_plan(plan_helper::get_plan_id($USER->id)); $ekenabled = $plan["enableek"]; - foreach ($courses["data"] as $course) { - if ($course["enabled"] == course_helper::DISABLED_COURSE) { + foreach ($courses as $course) { + if (!$course->enabled) { continue; } $modules = array_merge( - modules_helper::get_all_course_modules($course['courseid'], $userid, $ekenabled), + modules_helper::get_all_course_modules($course->courseid, $USER->id, $ekenabled), $modules ); } return $modules; } - public static function get_all_modules_returns() { + /** + * Returns the structure of the module array. + * @return external_multiple_structure + */ + public static function get_all_modules_returns(): external_multiple_structure { return new external_multiple_structure( - modules_helper::structure(), + modules_helper::structure(), ); } } diff --git a/lbplanner/services/modules/get_module.php b/lbplanner/services/modules/get_module.php index 4f66f036..a57c33d4 100644 --- a/lbplanner/services/modules/get_module.php +++ b/lbplanner/services/modules/get_module.php @@ -18,6 +18,7 @@ use external_api; use external_function_parameters; +use external_single_structure; use external_value; use local_lbplanner\helpers\modules_helper; use local_lbplanner\helpers\plan_helper; @@ -25,30 +26,50 @@ /** * Get the data for a module. + * + * @package local_lbplanner + * @subpackage services_modules + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class modules_get_module extends external_api { - public static function get_module_parameters() { - return new external_function_parameters(array( + /** + * Parameters for get_module. + * @return external_function_parameters + */ + public static function get_module_parameters(): external_function_parameters { + return new external_function_parameters([ 'moduleid' => new external_value(PARAM_INT, 'The id of the module', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + ]); } - public static function get_module($moduleid, $userid) { + /** + * Returns the data for a module + * + * @param int $moduleid The ID of the course + * @param int $userid The ID of the user + * @return array the module + */ + public static function get_module(int $moduleid, int $userid): array { global $DB; - self::validate_parameters(self::get_module_parameters(), array('moduleid' => $moduleid, 'userid' => $userid)); + self::validate_parameters(self::get_module_parameters(), ['moduleid' => $moduleid, 'userid' => $userid]); user_helper::assert_access($userid); - if (!$DB->record_exists(modules_helper::ASSIGN_TABLE, array('id' => $moduleid))) { + if (!$DB->record_exists(modules_helper::MDL_ASSIGN_TABLE, ['id' => $moduleid])) { throw new \moodle_exception('Module not found'); } return modules_helper::get_module($moduleid, $userid); } - public static function get_module_returns() { + /** + * Returns the structure of the module. + * @return external_single_structure + */ + public static function get_module_returns(): external_single_structure { return modules_helper::structure(); } } diff --git a/lbplanner/services/notifications/get_all_notifications.php b/lbplanner/services/notifications/get_all_notifications.php index 06f52bde..36b71fb3 100644 --- a/lbplanner/services/notifications/get_all_notifications.php +++ b/lbplanner/services/notifications/get_all_notifications.php @@ -19,34 +19,39 @@ use external_api; use external_function_parameters; use external_multiple_structure; -use external_single_structure; -use external_value; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\notifications_helper; /** - * Get all the notifications of the given user. + * Get all the notifications of the user. + * + * @package local_lbplanner + * @subpackage services_notifications + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class notifications_get_all_notifications extends external_api { - public static function get_all_notifications_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'User ID', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + /** + * Parameters for get_all_notifications. + * @return external_function_parameters + */ + public static function get_all_notifications_parameters(): external_function_parameters { + return new external_function_parameters([]); } - public static function get_all_notifications($userid) { - global $DB; + /** + * Returns all the notifications of the user + * + * @return array + */ + public static function get_all_notifications(): array { + global $DB, $USER; - self::validate_parameters(self::get_all_notifications_parameters(), array('userid' => $userid)); + $dbnotifications = $DB->get_records(notifications_helper::LBPLANNER_NOTIFICATION_TABLE, ['userid' => $USER->id]); - user_helper::assert_access($userid); - - $dbnotifications = $DB->get_records(notifications_helper::TABLE, array('userid' => $userid)); - - $notifications = array(); + $notifications = []; foreach ($dbnotifications as $dbnotification) { - $notifications[] = array( + $notifications[] = [ 'status' => $dbnotification->status, 'type' => $dbnotification->type, 'info' => $dbnotification->info, @@ -54,24 +59,18 @@ public static function get_all_notifications($userid) { 'notificationid' => $dbnotification->id, 'timestamp' => $dbnotification->timestamp, 'timestamp_read' => $dbnotification->timestamp_read, - ); + ]; } return $notifications; } - public static function get_all_notifications_returns() { + /** + * Returns the structure of the array of notifications. + * @return external_multiple_structure + */ + public static function get_all_notifications_returns(): external_multiple_structure { return new external_multiple_structure( - new external_single_structure( - array( - 'status' => new external_value(PARAM_INT, 'The status of the notification {0: unread, 1: read}'), - 'type' => new external_value(PARAM_INT, 'The type of the event that triggered the notification'), - 'info' => new external_value(PARAM_INT, 'Additional information about the notification'), - 'userid' => new external_value(PARAM_INT, 'The ID of the user for whom the notification is for'), - 'notificationid' => new external_value(PARAM_INT, 'The ID of the notification', NULL_NOT_ALLOWED), - 'timestamp' => new external_value(PARAM_INT, 'The timestamp of the notification'), - 'timestamp_read' => new external_value(PARAM_INT, 'The timestamp of the notification when it was read'), - ) - ) + notifications_helper::structure() ); } } diff --git a/lbplanner/services/notifications/update_notification.php b/lbplanner/services/notifications/update_notification.php index 912cb2b0..64262fe6 100644 --- a/lbplanner/services/notifications/update_notification.php +++ b/lbplanner/services/notifications/update_notification.php @@ -18,71 +18,75 @@ use external_api; use external_function_parameters; -use external_single_structure; use external_value; -use local_lbplanner\helpers\user_helper; +use local_lbplanner\enums\NOTIF_STATUS; use local_lbplanner\helpers\notifications_helper; /** - * Update the notification status of the given user and id. + * Update the notification status. + * + * @package local_lbplanner + * @subpackage services_notifications + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class notifications_update_notification extends external_api { - public static function update_notification_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'User ID', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + /** + * Parameters for update_notification. + * @return external_function_parameters + */ + public static function update_notification_parameters(): external_function_parameters { + return new external_function_parameters([ 'status' => new external_value( PARAM_INT, - 'The status of the notification {0: unread, 1: read}', + 'notification status ' . NOTIF_STATUS::format(), VALUE_REQUIRED, null, NULL_NOT_ALLOWED ), - 'notificationid' => new external_value(PARAM_INT, 'The ID of the notification', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + 'notificationid' => + new external_value(PARAM_INT, 'ID of the notification to be updated', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); } - public static function update_notification($userid, $status, $notificationid) { + /** + * Update the notification status. + * + * @param int $status notification status + * @see notifications_helper + * @param int $notificationid ID of the notification to be updated + * @return void + * @throws \moodle_exception when the notification doesn't exist + */ + public static function update_notification(int $status, int $notificationid) { global $DB; self::validate_parameters( self::update_notification_parameters(), - array('userid' => $userid, 'status' => $status, 'notificationid' => $notificationid) + ['status' => $status, 'notificationid' => $notificationid] ); - user_helper::assert_access($userid); - - if (!$DB->record_exists(notifications_helper::TABLE, array('id' => $notificationid))) { + if (!$DB->record_exists(notifications_helper::LBPLANNER_NOTIFICATION_TABLE, ['id' => $notificationid])) { throw new \moodle_exception('Notification does not exist'); } - $notification = $DB->get_record(notifications_helper::TABLE, array('id' => $notificationid), '*', MUST_EXIST); + $notification = $DB->get_record( + notifications_helper::LBPLANNER_NOTIFICATION_TABLE, + ['id' => $notificationid], + '*', + MUST_EXIST + ); $notification->status = $status; $notification->timestamp_read = time(); - $DB->update_record(notifications_helper::TABLE, $notification); - - return array( - 'status' => $notification->status, - 'type' => $notification->type, - 'info' => $notification->info, - 'userid' => $notification->userid, - 'notificationid' => $notification->id, - 'timestamp' => $notification->timestamp, - 'timestamp_read' => $notification->timestamp_read, - ); + $DB->update_record(notifications_helper::LBPLANNER_NOTIFICATION_TABLE, $notification); } + /** + * Returns the structure of nothing. + * @return null + */ public static function update_notification_returns() { - return new external_single_structure( - array( - 'status' => new external_value(PARAM_INT, 'The status of the notification {0: unread, 1: read}'), - 'type' => new external_value(PARAM_INT, 'The type of the event that triggered the notification'), - 'info' => new external_value(PARAM_INT, 'Additional information about the notification'), - 'userid' => new external_value(PARAM_INT, 'The ID of the user for whom the notification is for'), - 'notificationid' => new external_value(PARAM_INT, 'The ID of the notification'), - 'timestamp' => new external_value(PARAM_INT, 'The timestamp of the notification'), - 'timestamp_read' => new external_value(PARAM_INT, 'The timestamp of the notification'), - ) - ); + return null; } } diff --git a/lbplanner/services/plan/accept_invite.php b/lbplanner/services/plan/accept_invite.php index c0105850..7729818d 100644 --- a/lbplanner/services/plan/accept_invite.php +++ b/lbplanner/services/plan/accept_invite.php @@ -18,47 +18,58 @@ use external_api; use external_function_parameters; -use external_single_structure; use external_value; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\plan_helper; use local_lbplanner\helpers\notifications_helper; +use local_lbplanner\enums\{PLAN_ACCESS_TYPE, PLAN_INVITE_STATE, NOTIF_TRIGGER}; /** - * Update a invite from the plan. + * Accept an invite to the plan. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_accept_invite extends external_api { - public static function accept_invite_parameters() { - return new external_function_parameters(array( - 'inviteid' => new external_value(PARAM_INT, 'The id of the plan', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'userid' => new external_value(PARAM_INT, 'The id of the invited user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED) - )); + /** + * Parameters for accept_invite. + * @return external_function_parameters + */ + public static function accept_invite_parameters(): external_function_parameters { + return new external_function_parameters([ + 'inviteid' => new external_value(PARAM_INT, 'the ID of the invite to be accepted', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); } - public static function accept_invite($inviteid, $userid) { - global $DB; - - self::validate_parameters(self::accept_invite_parameters(), array( + /** + * Accepts an invite + * + * @param int $inviteid the ID of the invite to be accepted + * @return void + * @throws \moodle_exception when invite not found, already accepted or declined + */ + public static function accept_invite(int $inviteid) { + global $DB, $USER; + + self::validate_parameters(self::accept_invite_parameters(), [ 'inviteid' => $inviteid, - 'userid' => $userid, - )); - - user_helper::assert_access($userid); + ]); - if (!$DB->record_exists(plan_helper::INVITES_TABLE, array('id' => $inviteid, 'inviteeid' => $userid))) { + if (!$DB->record_exists(plan_helper::INVITES_TABLE, ['id' => $inviteid, 'inviteeid' => $USER->id])) { throw new \moodle_exception('Invite not found'); } if (!$DB->record_exists(plan_helper::INVITES_TABLE, - array( 'id' => $inviteid, 'inviteeid' => $userid, 'status' => plan_helper::INVITE_PENDING))) { + [ 'id' => $inviteid, 'inviteeid' => $USER->id, 'status' => PLAN_INVITE_STATE::PENDING])) { throw new \moodle_exception('Invite already accepted or declined'); } $invite = $DB->get_record(plan_helper::INVITES_TABLE, - array( + [ 'id' => $inviteid, - 'inviteeid' => $userid, - 'status' => plan_helper::INVITE_PENDING, - ), + 'inviteeid' => $USER->id, + 'status' => PLAN_INVITE_STATE::PENDING, + ], '*', MUST_EXIST ); @@ -67,75 +78,61 @@ public static function accept_invite($inviteid, $userid) { notifications_helper::notify_user( $invite->inviterid, $invite->id, - notifications_helper::TRIGGER_INVITE_ACCEPTED + NOTIF_TRIGGER::INVITE_ACCEPTED ); - // If the User is the User has Member in his plan, then removes it. - $oldplanid = plan_helper::get_plan_id($userid); - if (plan_helper::get_owner($oldplanid) == $userid) { + // Deletes the old plan if the user is the owner of it. + $oldplanid = plan_helper::get_plan_id($USER->id); + if (plan_helper::get_owner($oldplanid) == $USER->id) { foreach (plan_helper::get_plan_members($oldplanid) as $member) { - if ($member->userid != $userid) { - self::call_external_function('local_lbplanner_plan_remove_user', array( + if ($member->userid != $USER->id) { + self::call_external_function('local_lbplanner_plan_remove_user', [ 'planid' => $oldplanid, - 'userid' => $member->userid - )); + 'userid' => $member->userid, + ]); } } - self::call_external_function('local_lbplanner_plan_clear_plan', array( + // TODO: replace with helper function. + self::call_external_function('local_lbplanner_plan_clear_plan', [ 'planid' => $oldplanid, - 'userid' => $userid - )); - $DB->delete_records(plan_helper::TABLE, array('id' => $oldplanid)); + 'userid' => $USER->id, + ]); + $DB->delete_records(plan_helper::TABLE, ['id' => $oldplanid]); } // Updates the plan access. $planaccess = $DB->get_record( plan_helper::ACCESS_TABLE, - array( + [ 'planid' => $oldplanid, - 'userid' => $userid - ), + 'userid' => $USER->id, + ], '*', MUST_EXIST ); - $invite->status = plan_helper::INVITE_ACCEPTED; + $invite->status = PLAN_INVITE_STATE::ACCEPTED; $DB->update_record(plan_helper::INVITES_TABLE, $invite); - $planaccess->accesstype = plan_helper::ACCESS_TYPE_READ; + $planaccess->accesstype = PLAN_ACCESS_TYPE::READ; $planaccess->planid = $invite->planid; $DB->update_record(plan_helper::ACCESS_TABLE, $planaccess); - $invites = plan_helper::get_invites_send($userid); + $invites = plan_helper::get_invites_send($USER->id); foreach ($invites as $invite) { - if ($invite->status == plan_helper::INVITE_PENDING) { - $invite->status = plan_helper::INVITE_EXPIRED; + if ($invite->status == PLAN_INVITE_STATE::PENDING) { + $invite->status = PLAN_INVITE_STATE::EXPIRED; $DB->update_record(plan_helper::INVITES_TABLE, $invite); } } - - return array( - 'id' => $invite->id, - 'inviterid' => $invite->inviterid, - 'inviteeid' => $invite->inviteeid, - 'planid' => $invite->planid, - 'status' => $invite->status, - 'timestamp' => $invite->timestamp, - ); } - + /** + * Returns the structure of nothing. + * @return null + */ public static function accept_invite_returns() { - return new external_single_structure( - array( - 'id' => new external_value(PARAM_INT, 'The id of the invite'), - 'inviterid' => new external_value(PARAM_INT, 'The id of the owner user'), - 'inviteeid' => new external_value(PARAM_INT, 'The id of the invited user'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan'), - 'status' => new external_value(PARAM_INT, 'The Status of the invitation'), - 'timestamp' => new external_value(PARAM_INT, 'The time when the invitation was send'), - ) - ); + return null; } } diff --git a/lbplanner/services/plan/add_deadline.php b/lbplanner/services/plan/add_deadline.php deleted file mode 100644 index 3dd6329c..00000000 --- a/lbplanner/services/plan/add_deadline.php +++ /dev/null @@ -1,110 +0,0 @@ -. - -namespace local_lbplanner_services; - -use external_api; -use external_function_parameters; -use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\plan_helper; - -/** - * Add a deadline to the plan. - */ -class plan_add_deadline extends external_api { - public static function add_deadline_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The ID of the Plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'moduleid' => new external_value( - PARAM_INT, - 'The ID of the Module', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'deadlinestart' => new external_value( - PARAM_INT, - 'The Start of the Module', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'deadlineend' => new external_value( - PARAM_INT, - 'The End of the Module', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); - } - - public static function add_deadline($userid, $planid, $moduleid, $deadlinestart, $deadlineend) { - global $DB; - - self::validate_parameters( - self::add_deadline_parameters(), - array( - 'userid' => $userid, - 'planid' => $planid, - 'moduleid' => $moduleid, - 'deadlinestart' => $deadlinestart, - 'deadlineend' => $deadlineend, - ) - ); - - user_helper::assert_access($userid); - - if ( !plan_helper::check_edit_permissions( $planid, $userid ) ) { - throw new \moodle_exception('Access denied'); - } - - if ($DB->record_exists(plan_helper::DEADLINES_TABLE, array('moduleid' => $moduleid, 'planid' => $planid))) { - throw new \moodle_exception('Deadline already exists'); - } - - $deadline = new \stdClass(); - - $deadline->planid = $planid; - $deadline->moduleid = $moduleid; - $deadline->deadlinestart = $deadlinestart; - $deadline->deadlineend = $deadlineend; - - $DB->insert_record(plan_helper::DEADLINES_TABLE, $deadline); - - $plan = $DB->get_record(plan_helper::TABLE, array('id' => $planid)); - - return plan_helper::get_plan($planid); - } - - public static function add_deadline_returns() { - return plan_helper::plan_structure(); - } -} diff --git a/lbplanner/services/plan/clear_plan.php b/lbplanner/services/plan/clear_plan.php index 30755937..6a90fd97 100644 --- a/lbplanner/services/plan/clear_plan.php +++ b/lbplanner/services/plan/clear_plan.php @@ -18,52 +18,48 @@ use external_api; use external_function_parameters; -use external_value; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; /** * Clear the plan for the given user. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_clear_plan extends external_api { - public static function clear_plan_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The id of the plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); + /** + * Parameters for clear_plan. + * @return external_function_parameters + */ + public static function clear_plan_parameters(): external_function_parameters { + return new external_function_parameters([]); } - public static function clear_plan($userid, $planid) { - global $DB; + /** + * Clear the plan. + * + * @return void + * @throws Exception when access denied + */ + public static function clear_plan() { + global $DB, $USER; - self::validate_parameters(self::clear_plan_parameters(), array('userid' => $userid, 'planid' => $planid)); + $planid = plan_helper::get_plan_id($USER->id); - user_helper::assert_access($userid); - - if (!plan_helper::check_edit_permissions($planid, $userid)) { + if (!plan_helper::check_edit_permissions($planid, $USER->id)) { throw new \Exception('Access denied'); } - $DB->delete_records(plan_helper::DEADLINES_TABLE, array('planid' => $planid )); - - $plan = $DB->get_record(plan_helper::TABLE, array('id' => $planid)); - - return plan_helper::get_plan($planid); + $DB->delete_records(plan_helper::DEADLINES_TABLE, ['planid' => $planid ]); } + /** + * Returns the structure of nothing. + * @return null + */ public static function clear_plan_returns() { - return plan_helper::plan_structure(); + return null; } } diff --git a/lbplanner/services/plan/decline_invite.php b/lbplanner/services/plan/decline_invite.php index 67341ca5..044672e9 100644 --- a/lbplanner/services/plan/decline_invite.php +++ b/lbplanner/services/plan/decline_invite.php @@ -18,83 +18,78 @@ use external_api; use external_function_parameters; -use external_single_structure; use external_value; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\plan_helper; use local_lbplanner\helpers\notifications_helper; +use local_lbplanner\enums\{NOTIF_TRIGGER, PLAN_INVITE_STATE}; /** - * Update a invite from the plan. + * Decline an invite from the plan. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_decline_invite extends external_api { - public static function decline_invite_parameters() { - return new external_function_parameters(array( - 'inviteid' => new external_value(PARAM_INT, 'The inviteid of the plan', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'userid' => new external_value(PARAM_INT, 'The id of the invited user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED) - )); + /** + * Parameters for decline_invite. + * @return external_function_parameters + */ + public static function decline_invite_parameters(): external_function_parameters { + return new external_function_parameters([ + 'inviteid' => new external_value(PARAM_INT, 'the ID of the invite to be declined', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); } - public static function decline_invite($inviteid, $userid) { - global $DB; + /** + * Decline an invite. + * + * @param int $inviteid the ID of the invite to be declined + * @return void + * @throws \moodle_exception when invite not found, already accepted or declined + */ + public static function decline_invite(int $inviteid) { + global $DB, $USER; - self::validate_parameters(self::decline_invite_parameters(), array( + self::validate_parameters(self::decline_invite_parameters(), [ 'inviteid' => $inviteid, - 'userid' => $userid, - )); + ]); - user_helper::assert_access($userid); - - if (!$DB->record_exists(plan_helper::INVITES_TABLE, array('id' => $inviteid, 'inviteeid' => $userid))) { + if (!$DB->record_exists(plan_helper::INVITES_TABLE, ['id' => $inviteid, 'inviteeid' => $USER->id])) { throw new \moodle_exception('Invite not found'); } - if (!$DB->record_exists(plan_helper::INVITES_TABLE, - array('id' => $inviteid, 'inviteeid' => $userid, 'status' => plan_helper::INVITE_PENDING))) { - throw new \moodle_exception('Invite already accepted or declined'); - } $invite = $DB->get_record(plan_helper::INVITES_TABLE, - array( + [ 'id' => $inviteid, - 'inviteeid' => $userid, - 'status' => plan_helper::INVITE_PENDING, - ), + 'inviteeid' => $USER->id, + ], '*', MUST_EXIST ); + if ($invite->status !== PLAN_INVITE_STATE::PENDING) { + throw new \moodle_exception('Invite already accepted or declined'); + } + // Notify the user that invite has been declined. notifications_helper::notify_user( $invite->inviterid, $invite->id, - notifications_helper::TRIGGER_INVITE_DECLINED + NOTIF_TRIGGER::INVITE_DECLINED ); - $invite->status = plan_helper::INVITE_DECLINED; + $invite->status = PLAN_INVITE_STATE::DECLINED; $DB->update_record(plan_helper::INVITES_TABLE, $invite); - - return array( - 'id' => $invite->id, - 'inviterid' => $invite->inviterid, - 'inviteeid' => $invite->inviteeid, - 'planid' => $invite->planid, - 'status' => $invite->status, - 'timestamp' => $invite->timestamp, - ); } - + /** + * Returns the structure of nothing. + * @return null + */ public static function decline_invite_returns() { - return new external_single_structure( - array( - 'id' => new external_value(PARAM_INT, 'The id of the invite'), - 'inviterid' => new external_value(PARAM_INT, 'The id of the owner user'), - 'inviteeid' => new external_value(PARAM_INT, 'The id of the invited user'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan'), - 'status' => new external_value(PARAM_INT, 'The Status of the invitation'), - 'timestamp' => new external_value(PARAM_INT, 'The time when the invitation was send'), - ) - ); + return null; } } diff --git a/lbplanner/services/plan/delete_deadline.php b/lbplanner/services/plan/delete_deadline.php index 971bfb4d..d9a9d215 100644 --- a/lbplanner/services/plan/delete_deadline.php +++ b/lbplanner/services/plan/delete_deadline.php @@ -20,68 +20,69 @@ use external_function_parameters; use external_value; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; /** - * Delete a deadline from the plan. + * Delete a deadline from your plan + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_delete_deadline extends external_api { - public static function delete_deadline_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The ID of the Plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), + /** + * Parameters for delete_deadline. + * @return external_function_parameters + */ + public static function delete_deadline_parameters(): external_function_parameters { + return new external_function_parameters([ 'moduleid' => new external_value( PARAM_INT, - 'The ID of the Module', + 'ID of the Module', VALUE_REQUIRED, null, NULL_NOT_ALLOWED ), - )); + ]); } - public static function delete_deadline($userid, $planid, $moduleid) { - global $DB; + /** + * Delete a deadline. + * + * @param int $moduleid ID of the Module + * @return void + * @throws Exception when access denied + */ + public static function delete_deadline(int $moduleid) { + global $DB, $USER; self::validate_parameters( self::delete_deadline_parameters(), - array( - 'userid' => $userid, - 'planid' => $planid, + [ 'moduleid' => $moduleid, - ) + ] ); - user_helper::assert_access($userid); + $planid = plan_helper::get_plan_id($USER->id); - if (!plan_helper::check_edit_permissions($planid, $userid)) { + if (!plan_helper::check_edit_permissions($planid, $USER->id)) { throw new \Exception('Access denied'); } $DB->delete_records( plan_helper::DEADLINES_TABLE, - array( - 'planid' => $planid , - 'moduleid' => $moduleid - ) + [ + 'planid' => $planid, + 'moduleid' => $moduleid, + ] ); - - return plan_helper::get_plan($planid); } + /** + * Returns the structure of nothing. + * @return null + */ public static function delete_deadline_returns() { - return plan_helper::plan_structure(); + return null; } } diff --git a/lbplanner/services/plan/get_access.php b/lbplanner/services/plan/get_access.php deleted file mode 100644 index d209ca76..00000000 --- a/lbplanner/services/plan/get_access.php +++ /dev/null @@ -1,70 +0,0 @@ -. - -namespace local_lbplanner_services; - -use external_api; -use external_function_parameters; -use external_single_structure; -use external_value; -use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; - -/** - * Get the access type to the plan. - */ -class plan_get_access extends external_api { - public static function get_access_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The id of the plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); - } - - public static function get_access($userid, $planid) { - self::validate_parameters(self::get_access_parameters(), array('userid' => $userid, 'planid' => $planid)); - - user_helper::assert_access($userid); - - return array( - 'accesstype' => plan_helper::get_access_type($userid, $planid), - 'planid' => $planid, - 'userid' => $userid, - ); - } - - public static function get_access_returns() { - return new external_single_structure( - array( - 'accesstype' => new external_value(PARAM_INT, 'The type of access the user has to the plan'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan'), - 'userid' => new external_value(PARAM_INT, 'The id of the plan'), - ) - ); - } -} diff --git a/lbplanner/services/plan/get_invites.php b/lbplanner/services/plan/get_invites.php index ca2e02db..dbeddf01 100644 --- a/lbplanner/services/plan/get_invites.php +++ b/lbplanner/services/plan/get_invites.php @@ -18,80 +18,72 @@ use external_api; use external_function_parameters; -use external_single_structure; use external_multiple_structure; -use external_value; -use local_lbplanner\helpers\user_helper; +use local_lbplanner\helpers\invite_helper; use local_lbplanner\helpers\plan_helper; /** - * Get all the invites of the given user. + * Get all the invites of the current user. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_get_invites extends external_api { - public static function get_invites_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the Owner of the plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); + /** + * Parameters for get_invites. + * @return external_function_parameters + */ + public static function get_invites_parameters(): external_function_parameters { + return new external_function_parameters([]); } - public static function get_invites($userid) { - global $DB; + /** + * Returns all invites of the current user. + * + * @return array + */ + public static function get_invites(): array { + global $DB, $USER; - self::validate_parameters( - self::get_invites_parameters(), - array('userid' => $userid) - ); - - user_helper::assert_access($userid); - - $invitesreceived = $DB->get_records(plan_helper::INVITES_TABLE, array('inviteeid' => $userid)); - $invitessent = $DB->get_records(plan_helper::INVITES_TABLE, array('inviterid' => $userid)); + $invitesreceived = $DB->get_records(plan_helper::INVITES_TABLE, ['inviteeid' => $USER->id]); + $invitessent = $DB->get_records(plan_helper::INVITES_TABLE, ['inviterid' => $USER->id]); - $invites = array(); + $invites = []; foreach ($invitesreceived as $invite) { - $invites[] = array( + $invites[] = [ 'id' => $invite->id, 'inviterid' => $invite->inviterid, 'inviteeid' => $invite->inviteeid, 'planid' => $invite->planid, 'status' => $invite->status, 'timestamp' => $invite->timestamp, - ); + ]; } foreach ($invitessent as $invitesent) { - $invites[] = array( + $invites[] = [ 'id' => $invitesent->id, 'inviterid' => $invitesent->inviterid, 'inviteeid' => $invitesent->inviteeid, 'planid' => $invitesent->planid, 'status' => $invitesent->status, 'timestamp' => $invitesent->timestamp, - ); + ]; } return $invites; } - public static function get_invites_returns() { + /** + * Returns the structure of the array of invites. + * @return external_multiple_structure + */ + public static function get_invites_returns(): external_multiple_structure { return new external_multiple_structure( - new external_single_structure( - array( - 'id' => new external_value(PARAM_INT, 'The id of the invite'), - 'inviterid' => new external_value(PARAM_INT, 'The id of the owner user'), - 'inviteeid' => new external_value(PARAM_INT, 'The id of the invited user'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan'), - 'status' => new external_value(PARAM_INT, 'The Status of the invitation'), - 'timestamp' => new external_value(PARAM_INT, 'The time when the invitation was send'), - ) - ) + invite_helper::structure() ); } } diff --git a/lbplanner/services/plan/get_plan.php b/lbplanner/services/plan/get_plan.php index e5aa3693..822c2e82 100644 --- a/lbplanner/services/plan/get_plan.php +++ b/lbplanner/services/plan/get_plan.php @@ -18,41 +18,44 @@ use external_api; use external_function_parameters; -use external_value; +use external_single_structure; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; /** * Get the plan of the given user. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_get_plan extends external_api { - public static function get_plan_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); + /** + * Parameters for get_plan. + * @return external_function_parameters + */ + public static function get_plan_parameters(): external_function_parameters { + return new external_function_parameters([]); } - public static function get_plan($userid) { - global $DB; + /** + * Returns the plan of the current user. + * + * @return array + */ + public static function get_plan(): array { + global $DB, $USER; - self::validate_parameters(self::get_plan_parameters(), array('userid' => $userid)); - - user_helper::assert_access($userid); - - $planid = plan_helper::get_plan_id($userid); - - $plan = $DB->get_record(plan_helper::TABLE, array('id' => $planid)); + $planid = plan_helper::get_plan_id($USER->id); return plan_helper::get_plan($planid); } - public static function get_plan_returns() { + /** + * Returns the structure of the plan. + * @return external_single_structure + */ + public static function get_plan_returns(): external_single_structure { return plan_helper::plan_structure(); } } diff --git a/lbplanner/services/plan/invite_user.php b/lbplanner/services/plan/invite_user.php index 722800dd..e572fdf1 100644 --- a/lbplanner/services/plan/invite_user.php +++ b/lbplanner/services/plan/invite_user.php @@ -20,54 +20,57 @@ use external_function_parameters; use external_single_structure; use external_value; +use local_lbplanner\helpers\invite_helper; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\notifications_helper; +use local_lbplanner\enums\{NOTIF_TRIGGER, PLAN_INVITE_STATE}; /** - * Invite a user to the plan. + * Invite a user to the current user's plan + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_invite_user extends external_api { - public static function invite_user_parameters() { - return new external_function_parameters(array( - 'inviterid' => new external_value( - PARAM_INT, - 'The id of the Owner of the plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), + /** + * Parameters for invite_user. + * @return external_function_parameters + */ + public static function invite_user_parameters(): external_function_parameters { + return new external_function_parameters([ 'inviteeid' => new external_value( PARAM_INT, - 'The id of the user who gets invited', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The id of the plan', + 'ID of the user who gets invited', VALUE_REQUIRED, null, NULL_NOT_ALLOWED ), - )); + ]); } - public static function invite_user($inviterid, $inviteeid , $planid) { - global $DB; + /** + * Invite a user to the current user's plan + * + * @param int $inviteeid ID of the user who gets invited + * @return mixed the newly created invite + */ + public static function invite_user(int $inviteeid): mixed { + global $DB, $USER; self::validate_parameters( self::invite_user_parameters(), - array('inviterid' => $inviterid, 'inviteeid' => $inviteeid, 'planid' => $planid) + ['inviteeid' => $inviteeid] ); - user_helper::assert_access($inviterid); - if (plan_helper::get_owner($planid) != $inviterid) { + $planid = plan_helper::get_plan_id($USER->id); + + if (plan_helper::get_owner($planid) !== $USER->id) { throw new \moodle_exception('Access denied'); } - if ($inviterid == $inviteeid) { + if ($USER->id === $inviteeid) { throw new \moodle_exception('Cannot invite yourself'); } @@ -77,55 +80,36 @@ public static function invite_user($inviterid, $inviteeid , $planid) { if ($DB->record_exists( plan_helper::INVITES_TABLE, - array('inviteeid' => $inviteeid, 'planid' => $planid, 'status' => plan_helper::INVITE_PENDING) + ['inviteeid' => $inviteeid, 'planid' => $planid, 'status' => PLAN_INVITE_STATE::PENDING] )) { throw new \moodle_exception('User is already invited'); } - $invitee = user_helper::get_mdl_user_info($inviteeid); - $inviter = user_helper::get_mdl_user_info($inviterid); - - if ($invitee->address != $inviter->address) { - throw new \moodle_exception('Cannot invite user who is not in the same class'); - } - // Save the invite. $invite = new \stdClass(); $invite->planid = $planid; - $invite->inviterid = $inviterid; + $invite->inviterid = $USER->id; $invite->inviteeid = $inviteeid; $invite->timestamp = time(); - $invite->status = plan_helper::INVITE_PENDING; + $invite->status = PLAN_INVITE_STATE::PENDING; $invite->id = $DB->insert_record(plan_helper::INVITES_TABLE, $invite); - // Notifiy the invitee that he/she/it/they/xier/* has been invited. + // Notify the invitee that they've been invited. notifications_helper::notify_user( $inviteeid, $invite->id, - notifications_helper::TRIGGER_INVITE + NOTIF_TRIGGER::INVITE ); - return array( - 'id' => $invite->id, - 'inviterid' => $inviterid, - 'inviteeid' => $inviteeid, - 'planid' => $planid, - 'timestamp' => $invite->timestamp, - 'status' => $invite->status - ); + return $invite; } - public static function invite_user_returns() { - return new external_single_structure( - array( - 'id' => new external_value(PARAM_INT, 'The id of the invite'), - 'inviterid' => new external_value(PARAM_INT, 'The id of the owner user'), - 'inviteeid' => new external_value(PARAM_INT, 'The id of the invited user'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan'), - 'status' => new external_value(PARAM_INT, 'The status of the invitation'), - 'timestamp' => new external_value(PARAM_INT, 'The time when the invitation was send'), - ) - ); + /** + * Returns the structure of the invite. + * @return external_single_structure + */ + public static function invite_user_returns(): external_single_structure { + return invite_helper::structure(); } } diff --git a/lbplanner/services/plan/leave_plan.php b/lbplanner/services/plan/leave_plan.php index acc84fc3..5a7e9b28 100644 --- a/lbplanner/services/plan/leave_plan.php +++ b/lbplanner/services/plan/leave_plan.php @@ -18,59 +18,59 @@ use external_api; use external_function_parameters; -use external_value; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; use local_lbplanner\helpers\notifications_helper; +use local_lbplanner\enums\{NOTIF_TRIGGER, PLAN_ACCESS_TYPE, PLAN_INVITE_STATE}; /** - * Leave the plan of the given user. + * Leave your plan + * + * if no other user exists in the plan, the user can't leave + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_leave_plan extends external_api { - public static function leave_plan_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The id of the plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); + /** + * Parameters for leave_plan. + * @return external_function_parameters + */ + public static function leave_plan_parameters(): external_function_parameters { + return new external_function_parameters([]); } - public static function leave_plan($userid, $planid) { - global $DB; + /** + * Leave your plan + * + * @return void + * @throws \moodle_exception when user is only member left in plan + */ + public static function leave_plan() { + global $DB, $USER; - self::validate_parameters(self::leave_plan_parameters(), array('userid' => $userid, 'planid' => $planid)); + $planid = plan_helper::get_plan_id($USER->id); - user_helper::assert_access($userid); - - if (plan_helper::get_access_type($userid, $planid) == plan_helper::ACCESS_TYPE_NONE) { + // TODO: remove useless check. + if (plan_helper::get_access_type($USER->id, $planid) === PLAN_ACCESS_TYPE::NONE) { throw new \moodle_exception('User is not a member of this plan'); } - if (plan_helper::get_access_type($userid, $planid) == plan_helper::ACCESS_TYPE_OWNER) { + if (plan_helper::get_access_type($USER->id, $planid) === PLAN_ACCESS_TYPE::OWNER) { $members = plan_helper::get_plan_members($planid); if (count($members) == 1) { throw new \moodle_exception('Cannot Leave Plan: Plan must have at least one other member'); } - $writemembers = array(); - $allmembers = array(); + $writemembers = []; + $allmembers = []; foreach ($members as $member) { - if ($member->userid == $userid) { + if ($member->userid == $USER->id) { continue; } - if ($member->accesstype == plan_helper::ACCESS_TYPE_WRITE) { + if ($member->accesstype == PLAN_ACCESS_TYPE::WRITE) { $writemembers[] = $member; } $allmembers[] = $member; @@ -82,42 +82,45 @@ public static function leave_plan($userid, $planid) { } $newowneraccess = $DB->get_record( plan_helper::ACCESS_TABLE, - array('planid' => $planid, 'userid' => $newowner), '*', MUST_EXIST + ['planid' => $planid, 'userid' => $newowner], '*', MUST_EXIST ); - $newowneraccess->accesstype = plan_helper::ACCESS_TYPE_OWNER; + $newowneraccess->accesstype = PLAN_ACCESS_TYPE::OWNER; $DB->update_record(plan_helper::ACCESS_TABLE, $newowneraccess); } - $newplanid = plan_helper::copy_plan($planid, $userid); + $newplanid = plan_helper::copy_plan($planid, $USER->id); $oldaccess = $DB->get_record( plan_helper::ACCESS_TABLE, - array('planid' => $planid, 'userid' => $userid), '*', MUST_EXIST + ['planid' => $planid, 'userid' => $USER->id], '*', MUST_EXIST ); $oldaccess->planid = $newplanid; - $oldaccess->accesstype = plan_helper::ACCESS_TYPE_OWNER; + $oldaccess->accesstype = PLAN_ACCESS_TYPE::OWNER; $DB->update_record(plan_helper::ACCESS_TABLE, $oldaccess); // Notify plan owner that user has left his plan. - $invites = plan_helper::get_invites_send($userid); + $invites = plan_helper::get_invites_send($USER->id); foreach ($invites as $invite) { - if ($invite->status == plan_helper::INVITE_PENDING) { - $invite->status = plan_helper::INVITE_EXPIRED; + if ($invite->status == PLAN_INVITE_STATE::PENDING) { + $invite->status = PLAN_INVITE_STATE::EXPIRED; $DB->update_record(plan_helper::INVITES_TABLE, $invite); } } notifications_helper::notify_user( plan_helper::get_owner($planid), - $userid, - notifications_helper::TRIGGER_PLAN_LEFT + $USER->id, + NOTIF_TRIGGER::PLAN_LEFT ); - - return plan_helper::get_plan($planid, $userid); } + + /** + * Returns the structure of nothing. + * @return null + */ public static function leave_plan_returns() { - return plan_helper::plan_structure(); + return null; } } diff --git a/lbplanner/services/plan/remove_user.php b/lbplanner/services/plan/remove_user.php index 1341ad24..2cd75cfd 100644 --- a/lbplanner/services/plan/remove_user.php +++ b/lbplanner/services/plan/remove_user.php @@ -20,34 +20,50 @@ use external_function_parameters; use external_value; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; /** - * Remove a user from the plan. + * Remove a user from your plan + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_remove_user extends external_api { - public static function remove_user_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'removeuserid' => new external_value(PARAM_INT, 'The id of the user to remove', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'planid' => new external_value(PARAM_INT, 'The id of the plan', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + /** + * Parameters for remove_user. + * @return external_function_parameters + */ + public static function remove_user_parameters(): external_function_parameters { + return new external_function_parameters([ + 'userid' => new external_value(PARAM_INT, 'ID of the user to remove', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); } - public static function remove_user($userid, $removeuserid, $planid) { - global $DB; + /** + * Remove a user from your plan + * + * @param int $userid ID of the user to remove + * @return void + */ + public static function remove_user(int $userid) { + global $DB, $USER; self::validate_parameters( self::remove_user_parameters(), - array('userid' => $userid, 'removeuserid' => $removeuserid, 'planid' => $planid) + ['userid' => $userid] ); - user_helper::assert_access($userid); + $planid = plan_helper::get_plan_id($USER->id); - return plan_helper::remove_user($planid, $userid, $removeuserid); + plan_helper::remove_user($planid, $USER->id, $userid); } + /** + * Returns the structure of nothing. + * @return null + */ public static function remove_user_returns() { - return plan_helper::plan_structure(); + return null; } } diff --git a/lbplanner/services/plan/set_deadline.php b/lbplanner/services/plan/set_deadline.php new file mode 100644 index 00000000..aac4d86d --- /dev/null +++ b/lbplanner/services/plan/set_deadline.php @@ -0,0 +1,119 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use external_value; +use local_lbplanner\helpers\plan_helper; + +/** + * Set the deadline for a module. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class plan_set_deadline extends external_api { + /** + * Parameters for set_deadline. + * @return external_function_parameters + */ + public static function set_deadline_parameters(): external_function_parameters { + return new external_function_parameters([ + 'moduleid' => new external_value( + PARAM_INT, + 'ID of the module the deadline is for', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'deadlinestart' => new external_value( + PARAM_INT, + 'Start of the deadline', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'deadlineend' => new external_value( + PARAM_INT, + 'End of the deadline', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + ]); + } + + /** + * Set the deadline for a module + * + * @param int $moduleid ID of the module the deadline is for + * @param int $deadlinestart Start of the deadline + * @param int $deadlineend End of the deadline + * @return void + * @throws \moodle_exception when access denied + */ + public static function set_deadline(int $moduleid, int $deadlinestart, int $deadlineend) { + global $DB, $USER; + + self::validate_parameters( + self::set_deadline_parameters(), + [ + 'moduleid' => $moduleid, + 'deadlinestart' => $deadlinestart, + 'deadlineend' => $deadlineend, + ] + ); + + $planid = plan_helper::get_plan_id($USER->id); + + if (!plan_helper::check_edit_permissions($planid, $USER->id)) { + throw new \moodle_exception('Access denied'); + } + + // If a deadline already exists. + if (!$DB->record_exists(plan_helper::DEADLINES_TABLE, ['moduleid' => $moduleid, 'planid' => $planid])) { + // Update the existing deadline. + $deadline = $DB->get_record(plan_helper::DEADLINES_TABLE, ['moduleid' => $moduleid, 'planid' => $planid]); + + $deadline->deadlinestart = $deadlinestart; + $deadline->deadlineend = $deadlineend; + + $DB->update_record(plan_helper::DEADLINES_TABLE, $deadline); + } else { + // Otherwise insert a new one. + $deadline = new \stdClass(); + + $deadline->planid = $planid; + $deadline->moduleid = $moduleid; + $deadline->deadlinestart = $deadlinestart; + $deadline->deadlineend = $deadlineend; + + $DB->insert_record(plan_helper::DEADLINES_TABLE, $deadline); + } + } + + /** + * Returns the structure of nothing. + * @return null + */ + public static function set_deadline_returns() { + return null; + } +} diff --git a/lbplanner/services/plan/update_access.php b/lbplanner/services/plan/update_access.php index dc49d370..a9ddd3ea 100644 --- a/lbplanner/services/plan/update_access.php +++ b/lbplanner/services/plan/update_access.php @@ -19,65 +19,71 @@ use external_api; use external_function_parameters; use external_value; +use local_lbplanner\enums\PLAN_ACCESS_TYPE; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; /** * Update the access of the plan. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_update_access extends external_api { - public static function update_access_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The id of the plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), + /** + * Parameters for update_access. + * @return external_function_parameters + */ + public static function update_access_parameters(): external_function_parameters { + return new external_function_parameters([ 'accesstype' => new external_value( PARAM_INT, - 'The access type', + 'New access type '.PLAN_ACCESS_TYPE::format(), VALUE_REQUIRED, null, NULL_NOT_ALLOWED ), 'memberid' => new external_value( PARAM_INT, - 'The id of the member', + 'ID of the member to have their access changed', VALUE_REQUIRED, null, NULL_NOT_ALLOWED ), - )); + ]); } - public static function update_access($userid, $planid, $accesstype, $memberid) { - global $DB; + /** + * Update the access of the plan. + * + * @param int $accesstype new access type + * @see PLAN_ACCESS_TYPE + * @param int $memberid ID of the member to have their access changed + * @return void + * @throws \moodle_exception when access denied, type not valid or insufficient permissions + */ + public static function update_access(int $accesstype, int $memberid) { + global $DB, $USER; self::validate_parameters( self::update_access_parameters(), - array('userid' => $userid, 'planid' => $planid, 'accesstype' => $accesstype, 'memberid' => $memberid) + ['accesstype' => $accesstype, 'memberid' => $memberid] ); - user_helper::assert_access($userid); + $planid = plan_helper::get_plan_id($USER->id); - if (plan_helper::get_owner($planid) != $userid) { + if (plan_helper::get_owner($planid) !== $USER->id) { throw new \moodle_exception('Access denied'); } - if ($accesstype < 0 || $accesstype > 2) { + $accesstypeobj = PLAN_ACCESS_TYPE::try_from($accesstype); + + if ($accesstypeobj === null) { throw new \moodle_exception('Access type not valid'); } - if ($userid == $memberid) { + if ($USER->id === $memberid) { throw new \moodle_exception('Cannot change own permissions'); } @@ -85,19 +91,21 @@ public static function update_access($userid, $planid, $accesstype, $memberid) { throw new \moodle_exception('Cannot change permissions for the plan owner'); } - if ($accesstype == plan_helper::ACCESS_TYPE_OWNER) { - throw new \moodle_exception('Cannot change permissions to owner'); + if ($accesstypeobj === PLAN_ACCESS_TYPE::OWNER) { + throw new \moodle_exception('Cannot change permission to owner'); } - $access = $DB->get_record(plan_helper::ACCESS_TABLE, array('planid' => $planid, 'userid' => $memberid), '*', MUST_EXIST); + $access = $DB->get_record(plan_helper::ACCESS_TABLE, ['planid' => $planid, 'userid' => $memberid], '*', MUST_EXIST); $access->accesstype = $accesstype; $DB->update_record(plan_helper::ACCESS_TABLE, $access); - - return plan_helper::get_plan($planid); } + /** + * Returns the structure of nothing. + * @return null + */ public static function update_access_returns() { - return plan_helper::plan_structure(); + return null; } } diff --git a/lbplanner/services/plan/update_deadline.php b/lbplanner/services/plan/update_deadline.php deleted file mode 100644 index c85fe7cf..00000000 --- a/lbplanner/services/plan/update_deadline.php +++ /dev/null @@ -1,106 +0,0 @@ -. - -namespace local_lbplanner_services; - -use external_api; -use external_function_parameters; -use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\plan_helper; - -/** - * Update a deadline from the plan. - */ -class plan_update_deadline extends external_api { - public static function update_deadline_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The ID of the Plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'moduleid' => new external_value( - PARAM_INT, - 'The ID of the Module', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'deadlinestart' => new external_value( - PARAM_INT, - 'The start of the Module', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'deadlineend' => new external_value( - PARAM_INT, - 'The End of the Module', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); - } - - public static function update_deadline($userid, $planid, $moduleid, $deadlinestart, $deadlineend) { - global $DB; - - self::validate_parameters( - self::update_deadline_parameters(), - array( - 'userid' => $userid, - 'planid' => $planid, - 'moduleid' => $moduleid, - 'deadlinestart' => $deadlinestart, - 'deadlineend' => $deadlineend, - ) - ); - - user_helper::assert_access($userid); - - if (!plan_helper::check_edit_permissions($planid, $userid)) { - throw new \moodle_exception('Access denied'); - } - - if (!$DB->record_exists(plan_helper::DEADLINES_TABLE, array('moduleid' => $moduleid, 'planid' => $planid))) { - throw new \moodle_exception('Deadline doesnt exists'); - } - $deadline = $DB->get_record(plan_helper::DEADLINES_TABLE, array('moduleid' => $moduleid, 'planid' => $planid)); - - $deadline->deadlinestart = $deadlinestart; - $deadline->deadlineend = $deadlineend; - - $DB->update_record(plan_helper::DEADLINES_TABLE, $deadline); - - return plan_helper::get_plan($planid); - - } - - public static function update_deadline_returns() { - return plan_helper::plan_structure(); - } -} diff --git a/lbplanner/services/plan/update_invite.php b/lbplanner/services/plan/update_invite.php deleted file mode 100644 index 3b3640c9..00000000 --- a/lbplanner/services/plan/update_invite.php +++ /dev/null @@ -1,132 +0,0 @@ -. - -namespace local_lbplanner_services; - -use external_api; -use external_function_parameters; -use external_single_structure; -use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\notifications_helper; - -/** - * THIS METHOD IS NOT USED ANYMORE. JUST TO KEEP OLD CODE FOR REFERENCE. - */ -class plan_update_invite extends external_api { - public static function update_invite_parameters() { - return new external_function_parameters(array( - 'planid' => new external_value(PARAM_INT, 'The id of the plan', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'userid' => new external_value(PARAM_INT, 'The id of the invited user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'status' => new external_value(PARAM_INT, 'The status of the invite', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); - } - - public static function update_invite($planid, $userid, $status) { - global $DB; - - self::validate_parameters(self::update_invite_parameters(), array( - 'planid' => $planid, - 'userid' => $userid, - 'status' => $status, - )); - - user_helper::assert_access($userid); - - if ($status != plan_helper::INVITE_ACCEPTED && $status != plan_helper::INVITE_DECLINED) { - throw new \moodle_exception('Invalid status'); - } - - $invite = $DB->get_record(plan_helper::INVITES_TABLE, - array( - 'planid' => $planid, - 'inviteeid' => $userid, - ), - '*', - MUST_EXIST - ); - - if ($invite->status != plan_helper::INVITE_PENDING) { - throw new \moodle_exception('Invalid status'); - } - - $invite->status = $status; - - $DB->update_record(plan_helper::INVITES_TABLE, $invite); - - $trigger = $status == plan_helper::INVITE_ACCEPTED ? - notifications_helper::TRIGGER_INVITE_ACCEPTED - : notifications_helper::TRIGGER_INVITE_DECLINED; - - notifications_helper::notify_user($invite->inviterid, $userid , $trigger); - - // TODO: Change plan access and delete old plan if inivite is accepted. - - if ($status == plan_helper::INVITE_ACCEPTED) { - $oldplanid = plan_helper::get_plan_id($userid); - - if (plan_helper::get_owner($oldplanid) == $userid) { - - foreach (plan_helper::get_plan_members($oldplanid) as $member) { - if ($member->userid != $userid) { - plan_leave_plan::leave_plan($member->userid, $oldplanid); - } - } - self::call_external_function('local_lbplanner_plan_clear_plan', array ($userid, $oldplanid)); - - $DB->delete_records(plan_helper::TABLE, array('id' => $oldplanid)); - } - - $planaccess = $DB->get_record( - plan_helper::ACCESS_TABLE, - array( - 'planid' => $oldplanid, - 'userid' => $userid - ), - '*', - MUST_EXIST - ); - - $planaccess->accesstype = plan_helper::ACCESS_TYPE_READ; - $planaccess->planid = $planid; - - $DB->update_record(plan_helper::ACCESS_TABLE, $planaccess); - - $DB->delete_records(plan_helper::INVITES_TABLE, array('id' => $invite->id)); - } - return array( - 'inviterid' => $invite->inviterid, - 'inviteeid' => $invite->inviteeid, - 'planid' => $invite->planid, - 'status' => $invite->status, - 'timestamp' => $invite->timestamp, - ); - } - - - public static function update_invite_returns() { - return new external_single_structure( - array( - 'inviterid' => new external_value(PARAM_INT, 'The id of the owner user'), - 'inviteeid' => new external_value(PARAM_INT, 'The id of the invited user'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan'), - 'status' => new external_value(PARAM_INT, 'The Status of the invitation'), - 'timestamp' => new external_value(PARAM_INT, 'The time when the invitation was send'), - ) - ); - } -} diff --git a/lbplanner/services/plan/update_plan.php b/lbplanner/services/plan/update_plan.php index ade69cc5..586e4e42 100644 --- a/lbplanner/services/plan/update_plan.php +++ b/lbplanner/services/plan/update_plan.php @@ -20,71 +20,73 @@ use external_function_parameters; use external_value; use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; /** - * Update the plan of the given user. + * Update the plan details. + * + * @package local_lbplanner + * @subpackage services_plan + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class plan_update_plan extends external_api { - public static function update_plan_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( - PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - 'planid' => new external_value( - PARAM_INT, - 'The id of the plan', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), + /** + * Parameters for update_plan. + * @return external_function_parameters + */ + public static function update_plan_parameters(): external_function_parameters { + return new external_function_parameters([ 'planname' => new external_value( PARAM_TEXT, - 'The Name of the Plan', + 'Name of the Plan', VALUE_REQUIRED, null, NULL_NOT_ALLOWED ), 'enableek' => new external_value( PARAM_BOOL, - 'If the plan is enabled for ek', + 'Whether EK is enabled for the plan', VALUE_REQUIRED, null, NULL_NOT_ALLOWED ), - )); + ]); } - public static function update_plan($userid, $planid, $planname, $enableek) { - global $DB; + /** + * Update the plan details. + * + * @param string $planname Name of the Plan + * @param bool $enableek Whether EK is enabled for the plan + * @return void + * @throws Exception when access denied + */ + public static function update_plan(string $planname, bool $enableek) { + global $DB, $USER; self::validate_parameters( self::update_plan_parameters(), - array('userid' => $userid, 'planid' => $planid, 'planname' => $planname, 'enableek' => $enableek) + ['planname' => $planname, 'enableek' => $enableek] ); - user_helper::assert_access($userid); + $planid = plan_helper::get_plan_id($USER->id); - if (!plan_helper::check_edit_permissions($planid, $userid)) { + if (!plan_helper::check_edit_permissions($planid, $USER->id)) { throw new \Exception('Access denied'); } - $planid = plan_helper::get_plan_id($userid); - - $plan = $DB->get_record(plan_helper::TABLE, array('id' => $planid), '*', MUST_EXIST); + $plan = $DB->get_record(plan_helper::TABLE, ['id' => $planid], '*', MUST_EXIST); $plan->name = $planname; $plan->enableek = $enableek; $DB->update_record(plan_helper::TABLE, $plan); - - return plan_helper::get_plan($planid); } + /** + * Returns the structure of nothing. + * @return null + */ public static function update_plan_returns() { - return plan_helper::plan_structure(); + return null; } } diff --git a/lbplanner/services/slots/book_reservation.php b/lbplanner/services/slots/book_reservation.php new file mode 100644 index 00000000..d07725d7 --- /dev/null +++ b/lbplanner/services/slots/book_reservation.php @@ -0,0 +1,156 @@ +. + +namespace local_lbplanner_services; + +use DateTimeImmutable; + +use core_user; +use external_api; +use external_function_parameters; +use external_single_structure; +use external_value; + +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\reservation; + +/** + * Books a reservation for the user + * + * @package local_lbplanner + * @subpackage services_slots + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_book_reservation extends external_api { + /** + * Parameters for book_reservation. + * @return external_function_parameters + */ + public static function book_reservation_parameters(): external_function_parameters { + global $USER; + return new external_function_parameters([ + 'slotid' => new external_value( + PARAM_INT, + 'ID of the slot for which a reservation is being requested', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'date' => new external_value( + PARAM_TEXT, + 'date of the reservation in YYYY-MM-DD (as per ISO-8601)', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'userid' => new external_value( + PARAM_INT, + 'the user to reserve this slot for', + VALUE_DEFAULT, + $USER->id, + NULL_NOT_ALLOWED + ), + ]); + } + + /** + * Books a reservation + * @param int $slotid the slot to book a reservation for + * @param string $date the day this reservation should take place + * @param int $userid the user to reserve for + */ + public static function book_reservation(int $slotid, string $date, int $userid): array { + global $USER, $DB; + self::validate_parameters( + self::book_reservation_parameters(), + [ + 'slotid' => $slotid, + 'date' => $date, + 'userid' => $userid, + ] + ); + + $now = new DateTimeImmutable(); + $dateobj = DateTimeImmutable::createFromFormat("YY-MM-DD", $date); + $td = $dateobj->diff($now); + + if ($td->invert) { + throw new \moodle_exception('Can\'t reserve date in the past'); + } + + $maxdays = null; + $student = null; + + if ($userid === $USER->id) { + // Student reserving slot for themself. + + $maxdays = slot_helper::RESERVATION_RANGE_USER; + $student = $USER; + } else { + // Supervisor reserving slot for student. + + if (!slot_helper::check_slot_supervisor($USER->id, $slotid)) { + throw new \moodle_exception('Forbidden: you\'re not a supervisor of this slot'); + } + + $maxdays = slot_helper::RESERVATION_RANGE_USER; + $student = core_user::get_user($userid, '*', MUST_EXIST); + } + + if ($td->days > $maxdays) { + throw new \moodle_exception("Date is past allowed date ({$maxdays} days in the future)"); + } + + $slot = slot_helper::get_slot($slotid); + + // Check if user has access to slot. + if (count(slot_helper::filter_slots_for_user([$slot], $student)) === 0) { + throw new \moodle_exception('Student does not have access to this slot'); + } + + // Check if user is already in slot. + foreach (slot_helper::get_reservations_for_slot($slotid) as $tmpreservation) { + if ($tmpreservation->userid === $userid) { + throw new \moodle_exception('Student is already in slot'); + } + } + + // TODO: check if user is already in a different slot at the same time. + + // Check if slot is full. + if ($slot->get_fullness() >= $slot->size) { + throw new \moodle_exception('Slot is already full'); + } + + $reservation = new reservation(0, $slotid, $dateobj, $userid, $USER->id); + + $id = $DB->insert_record(slot_helper::TABLE_RESERVATIONS, $reservation->prepare_for_db()); + $reservation->set_fresh($id, $slot); + + // TODO: if userid!=USER->id → send notif to the user that the supervisor booked a reservation for them. + + return $reservation->prepare_for_api(); + } + + /** + * Returns the structure of the reservation + * @return external_single_structure + */ + public static function book_reservation_returns(): external_single_structure { + return reservation::api_structure(); + } +} diff --git a/lbplanner/services/slots/create_slot.php b/lbplanner/services/slots/create_slot.php new file mode 100644 index 00000000..30499923 --- /dev/null +++ b/lbplanner/services/slots/create_slot.php @@ -0,0 +1,144 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use external_single_structure; +use external_value; +use local_lbplanner\enums\WEEKDAY; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\slot; +use moodle_exception; + +/** + * Create a slot + * + * @package local_lbplanner + * @subpackage services_slots + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_create_slot extends external_api { + /** + * Parameters for create_slot. + * @return external_function_parameters + */ + public static function create_slot_parameters(): external_function_parameters { + // TODO: set hardcoded doc values with constants instead of hardcoded values. + return new external_function_parameters([ + 'startunit' => new external_value( + PARAM_INT, + 'The school unit this slot starts in, starting at 1 for 8:00 and ending at 16 for 21:00', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'duration' => new external_value( + PARAM_INT, + 'The amount of units this slot is long. startunit + duration may not exceed 16', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'weekday' => new external_value( + PARAM_INT, + 'The weekday this slot happens on. '.WEEKDAY::format(), + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'room' => new external_value( + PARAM_TEXT, + 'The room this slot happens in. max. 7 characters', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'size' => new external_value( + PARAM_INT, + 'How many pupils this slot can fit', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + ]); + } + + /** + * Create a slot + * @param int $startunit the unit this slot starts in + * @param int $duration how long the unit lasts for + * @param int $weekday which day of the week this slot is on + * @param string $room which room this slot is for + * @param int $size how many pupils this slot can fit + */ + public static function create_slot(int $startunit, int $duration, int $weekday, string $room, int $size): array { + global $DB; + self::validate_parameters( + self::create_slot_parameters(), + [ + 'startunit' => $startunit, + 'duration' => $duration, + 'weekday' => $weekday, + 'room' => $room, + 'size' => $size, + ] + ); + + // Validating startunit. + $maxunit = count(slot_helper::SCHOOL_UNITS) - 1; + if ($startunit < 1) { + throw new moodle_exception('can\'t have a start unit smaller than 1'); + } else if ($startunit > $maxunit) { + throw new moodle_exception("can't have a start unit larger than {$maxunit}"); + } + // Validating duration. + if ($duration < 1) { + throw new moodle_exception('duration must be at least 1'); + } else if ($startunit + $duration > $maxunit) { + throw new moodle_exception("slot goes past the max unit {$maxunit}"); + } + // Validating weekday. + WEEKDAY::from($weekday); + // Validating room. + if (strlen($room) <= 1) { + throw new moodle_exception('room name has to be at least 2 characters long'); + } else if (strlen($room) > slot_helper::ROOM_MAXLENGTH) { + throw new moodle_exception('room name has a maximum of '.slot_helper::ROOM_MAXLENGTH.' characters'); + } + // Validating size. + if ($size < 0) { + throw new moodle_exception('can\'t have a negative size for a slot'); + } + + // Actually inserting the slot. + $slot = new slot(0, $startunit, $duration, $weekday, $room, $size); + $id = $DB->insert_record(slot_helper::TABLE_SLOTS, $slot->prepare_for_db()); + $slot->set_fresh($id); + + return $slot->prepare_for_api(); + } + + /** + * Returns the structure of the slot + * @return external_single_structure + */ + public static function create_slot_returns(): external_single_structure { + return slot::api_structure(); + } +} diff --git a/lbplanner/services/slots/get_my_slots.php b/lbplanner/services/slots/get_my_slots.php new file mode 100644 index 00000000..e50f4206 --- /dev/null +++ b/lbplanner/services/slots/get_my_slots.php @@ -0,0 +1,67 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use external_multiple_structure; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\slot; + +/** + * Returns all slots the user can theoretically reserve. + * This does not include times the user has already reserved a slot for. + * + * @package local_lbplanner + * @subpackage services_slots + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_get_my_slots extends external_api { + /** + * Parameters for get_my_slots. + * @return external_function_parameters + */ + public static function get_my_slots_parameters(): external_function_parameters { + return new external_function_parameters([]); + } + + /** + * Returns slots the current user is supposed to see + */ + public static function get_my_slots(): array { + global $USER; + + $allslots = slot_helper::get_all_slots(); + + $myslots = slot_helper::filter_slots_for_user($allslots, $USER); + + $returnslots = slot_helper::filter_slots_for_time($myslots, slot_helper::RESERVATION_RANGE_USER); + + return array_map(fn(slot $slot) => $slot->prepare_for_api(), $returnslots); + } + + /** + * Returns the structure of the slot array + * @return external_multiple_structure + */ + public static function get_my_slots_returns(): external_multiple_structure { + return new external_multiple_structure( + slot::api_structure() + ); + } +} diff --git a/lbplanner/services/slots/get_student_slots.php b/lbplanner/services/slots/get_student_slots.php new file mode 100644 index 00000000..c423196a --- /dev/null +++ b/lbplanner/services/slots/get_student_slots.php @@ -0,0 +1,76 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use external_multiple_structure; +use external_value; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\helpers\user_helper; +use local_lbplanner\model\slot; + +/** + * Returns all slots a supervisor can theoretically reserve for a user. + * This does not include times the user has already reserved a slot for. + * + * @package local_lbplanner + * @subpackage services_slots + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_get_student_slots extends external_api { + /** + * Parameters for get_student_slots. + * @return external_function_parameters + */ + public static function get_student_slots_parameters(): external_function_parameters { + return new external_function_parameters([ + 'userid' => new external_value(PARAM_INT, 'ID of the user to query for', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + ]); + } + + /** + * Returns slots of a user the supervisor can see. + * @param int $userid ID of the user in question (NOT the supervisor) + */ + public static function get_student_slots(int $userid): array { + global $USER; + self::validate_parameters( + self::get_student_slots_parameters(), + ['userid' => $userid] + ); + + $superslots = slot_helper::get_supervisor_slots($USER->id); + + $myslots = slot_helper::filter_slots_for_user($superslots, user_helper::get_mdluser($userid)); + + $returnslots = slot_helper::filter_slots_for_time($myslots, slot_helper::RESERVATION_RANGE_SUPERVISOR); + + return array_map(fn(slot $slot) => $slot->prepare_for_api(), $returnslots); + } + + /** + * Returns the structure of the slot array + * @return external_multiple_structure + */ + public static function get_student_slots_returns(): external_multiple_structure { + return new external_multiple_structure( + slot::api_structure() + ); + } +} diff --git a/lbplanner/services/slots/get_supervisor_slots.php b/lbplanner/services/slots/get_supervisor_slots.php new file mode 100644 index 00000000..20278673 --- /dev/null +++ b/lbplanner/services/slots/get_supervisor_slots.php @@ -0,0 +1,62 @@ +. + +namespace local_lbplanner_services; + +use external_api; +use external_function_parameters; +use external_multiple_structure; +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\model\slot; + +/** + * Returns all slots a supervisor can see. + * + * @package local_lbplanner + * @subpackage services_slots + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_get_supervisor_slots extends external_api { + /** + * Parameters for get_supervisor_slots. + * @return external_function_parameters + */ + public static function get_supervisor_slots_parameters(): external_function_parameters { + return new external_function_parameters([]); + } + + /** + * Returns all slots a supervisor controls. + */ + public static function get_supervisor_slots(): array { + global $USER; + + $slots = slot_helper::get_supervisor_slots($USER->id); + + return array_map(fn(slot $slot) => $slot->prepare_for_api(), $slots); + } + + /** + * Returns the structure of the slot array + * @return external_multiple_structure + */ + public static function get_supervisor_slots_returns(): external_multiple_structure { + return new external_multiple_structure( + slot::api_structure() + ); + } +} diff --git a/lbplanner/services/slots/unbook_reservation.php b/lbplanner/services/slots/unbook_reservation.php new file mode 100644 index 00000000..2dd4da6e --- /dev/null +++ b/lbplanner/services/slots/unbook_reservation.php @@ -0,0 +1,119 @@ +. + +namespace local_lbplanner_services; + +use DateTimeImmutable; + +use external_api; +use external_function_parameters; +use external_value; + +use local_lbplanner\helpers\slot_helper; +use local_lbplanner\helpers\notifications_helper; +use local_lbplanner\enums\NOTIF_TRIGGER; + +/** + * Unbooks reservation + * + * @package local_lbplanner + * @subpackage services_slots + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class slots_unbook_reservation extends external_api { + /** + * Parameters for unbook_reservation. + * @return external_function_parameters + */ + public static function unbook_reservation_parameters(): external_function_parameters { + return new external_function_parameters([ + 'reservationid' => new external_value( + PARAM_INT, + 'ID of the reservation for which unbooking is being requested', + VALUE_REQUIRED, + null, + NULL_NOT_ALLOWED + ), + 'nice' => new external_value( + PARAM_BOOL, + 'whether to ask the student nicely to unbook themself via a notification', + VALUE_DEFAULT, + true, + NULL_NOT_ALLOWED + ), + ]); + } + + /** + * Tries to request unbooking + * @param int $reservationid which reservation to unbook + * @param bool $nice whether to ask the student to unbook themself, or force-unbook + */ + public static function unbook_reservation(int $reservationid, bool $nice): void { + global $USER, $DB; + self::validate_parameters( + self::unbook_reservation_parameters(), + [ + 'reservationid' => $reservationid, + 'nice' => $nice, + ] + ); + + $reservation = slot_helper::get_reservation($reservationid); + $now = new DateTimeImmutable(); + + $endpast = $now->diff($reservation->get_datetime_end())->invert === 1; + $startpast = $endpast || ($now->diff($reservation->get_datetime())->invert === 1); + + if ($USER->id === $reservation->userid) { + if ($startpast) { + throw new \moodle_exception('You can\'t unbook this reservation because it has already started'); + } + } else if (slot_helper::check_slot_supervisor($USER->id, $reservation->slotid)) { + if ($endpast) { + throw new \moodle_exception('You can\'t unbook this reservation because it has already ended'); + } + if ($nice) { + if ($startpast) { + throw new \moodle_exception( + 'Students can\'t unbook reservations that have already started.' + .' If you want to unbook this reservation regardless, force it.' + ); + } + notifications_helper::notify_user($reservation->userid, $reservation->id, NOTIF_TRIGGER::UNBOOK_REQUESTED); + return; + } else { + notifications_helper::notify_user($reservation->userid, $reservation->id, NOTIF_TRIGGER::UNBOOK_FORCED); + } + } else { + throw new \moodle_exception('insufficient permission to unbook this reservation'); + } + + $DB->delete_records( + slot_helper::TABLE_RESERVATIONS, + ['id' => $reservation->id] + ); + } + + /** + * Returns nothing at all + * @return null + */ + public static function unbook_reservation_returns() { + return null; + } +} diff --git a/lbplanner/services/user/delete_user.php b/lbplanner/services/user/delete_user.php index d099bf69..01c90bfc 100644 --- a/lbplanner/services/user/delete_user.php +++ b/lbplanner/services/user/delete_user.php @@ -16,80 +16,100 @@ namespace local_lbplanner_services; -use core_competency\plan; +use dml_exception; use external_api; use external_function_parameters; -use external_single_structure; use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\course_helper; -use local_lbplanner\helpers\notifications_helper; +use local_lbplanner\helpers\{user_helper, plan_helper, course_helper, notifications_helper}; +use local_lbplanner\enums\{PLAN_INVITE_STATE, PLAN_ACCESS_TYPE}; +use moodle_exception; /** - * Removes all user data stored by the lbplanner app + * Removes all user data stored by the lbplanner app. + * + * Admins can pass a userid to delete the user with the given id + * + * @package local_lbplanner + * @subpackage services_user + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class user_delete_user extends external_api { - public static function delete_user_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value( + /** + * Parameters for delete_user + * @return external_function_parameters + */ + public static function delete_user_parameters(): external_function_parameters { + global $USER; + return new external_function_parameters( + ['userid' => new external_value( PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED - ), - )); + 'The id of the user to delete', + VALUE_DEFAULT, + $USER->id, + NULL_NOT_ALLOWED, + )] + ); } + /** + * Removes all user data stored by the lbplanner app + * @param int $userid (optional) the id of the user to delete + * @throws dml_exception + * @throws moodle_exception + */ public static function delete_user($userid) { - global $DB; - self::validate_parameters(self::delete_user_parameters(), array('userid' => $userid)); + global $DB, $USER; + + self::validate_parameters(self::delete_user_parameters(), ['userid' => $userid]); - user_helper::assert_access($userid); + if (!user_helper::is_admin($USER->id)) { + user_helper::assert_access($userid); + } // Check if User is in user table. - if (!$DB->record_exists(user_helper::TABLE, array('userid' => $userid))) { - throw new \moodle_exception('Access denied'); + if (!$DB->record_exists(user_helper::LB_PLANNER_USER_TABLE, ['userid' => $userid])) { + throw new moodle_exception('User is not registered in LB Planner'); } $planid = plan_helper::get_plan_id($userid); - - if (plan_helper::get_access_type($planid, $userid) == plan_helper::ACCESS_TYPE_OWNER) { - if (plan_helper::get_plan_members($planid) > 1) { - self::call_external_function('local_lbplanner_plan_leave_plan', array('userid' => $userid, 'planid' => $planid)); - } else { - // Deleting Plan. - $DB->delete_records(plan_helper::DEADLINES_TABLE, array('planid' => $planid)); - $DB->delete_records(plan_helper::TABLE, array('id' => $planid)); - } + // Check if User is in a plan. If yes, leave the plan first then delete the plan. + // If the user is the only member of the plan, delete the plan. + if ( + !(count(plan_helper::get_plan_members($planid)) == 1 ) + && + !(plan_helper::get_access_type($planid, $userid) == PLAN_ACCESS_TYPE::OWNER)) { + self::call_external_function('local_lbplanner_plan_leave_plan', ['userid' => $userid, 'planid' => $planid]); } + $DB->delete_records(plan_helper::DEADLINES_TABLE, ['planid' => $planid]); + $DB->delete_records(plan_helper::TABLE, ['id' => $planid]); + // Delete all Notifications. - if ($DB->record_exists(notifications_helper::TABLE, array('userid' => $userid))) { - $DB->delete_records(notifications_helper::TABLE, array('userid' => $userid)); + if ($DB->record_exists(notifications_helper::LBPLANNER_NOTIFICATION_TABLE, ['userid' => $userid])) { + $DB->delete_records(notifications_helper::LBPLANNER_NOTIFICATION_TABLE, ['userid' => $userid]); } $invites = plan_helper::get_invites_send($userid); foreach ($invites as $invite) { - if ($invite->status == plan_helper::INVITE_PENDING) { - $invite->status = plan_helper::INVITE_EXPIRED; + if ($invite->status == PLAN_INVITE_STATE::PENDING) { + $invite->status = PLAN_INVITE_STATE::EXPIRED; $DB->update_record(plan_helper::INVITES_TABLE, $invite); } } // Deleting associating with the plan. - $DB->delete_records(plan_helper::ACCESS_TABLE, array('userid' => $userid)); + $DB->delete_records(plan_helper::ACCESS_TABLE, ['userid' => $userid]); // Deleting all Courses associated with the User. - $DB->delete_records(course_helper::LBPLANNER_COURSE_TABLE, array('userid' => $userid)); + $DB->delete_records(course_helper::LBPLANNER_COURSE_TABLE, ['userid' => $userid]); // Deleting User from User table. - $DB->delete_records(user_helper::TABLE, array('userid' => $userid)); - - return array('message' => 'User deleted successfully'); + $DB->delete_records(user_helper::LB_PLANNER_USER_TABLE, ['userid' => $userid]); } + /** + * Returns the structure of the data returned by the delete_user function + * @return external_multiple_structure + */ public static function delete_user_returns() { - return new external_single_structure( - array('message' => new external_value(PARAM_TEXT, 'The message to return to the user')) - ); + return null; } } diff --git a/lbplanner/services/user/get_all_users.php b/lbplanner/services/user/get_all_users.php index 6308f87b..69ce75b6 100644 --- a/lbplanner/services/user/get_all_users.php +++ b/lbplanner/services/user/get_all_users.php @@ -16,63 +16,73 @@ namespace local_lbplanner_services; +use dml_exception; use external_api; use external_function_parameters; use external_multiple_structure; -use external_single_structure; use external_value; +use invalid_parameter_exception; +use moodle_exception; + use local_lbplanner\helpers\user_helper; +use local_lbplanner\model\user; /** * Gets all users registered by the lbplanner app. + * + * @package local_lbplanner + * @subpackage services_user + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class user_get_all_users extends external_api { - public static function get_all_users_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); + /** + * Parameters for get_all_users + * @return external_function_parameters + */ + public static function get_all_users_parameters(): external_function_parameters { + return new external_function_parameters([ + 'vintage' => new external_value(PARAM_TEXT, 'The vintage to filter the users by', VALUE_DEFAULT, null), + ]); } - public static function get_all_users($userid) { - global $DB; + /** + * Gives back all users registered by the lbplanner app. + * @param string $vintage (optional) gives back all users with the given vintage + * @throws moodle_exception + * @throws dml_exception + * @throws invalid_parameter_exception + */ + public static function get_all_users(string $vintage): array { + global $DB, $USER; - self::validate_parameters(self::get_all_users_parameters(), array('userid' => $userid)); + self::validate_parameters(self::get_all_users_parameters(), ['vintage' => $vintage]); // Check if token is allowed to access this function. - user_helper::assert_access($userid); + user_helper::assert_access($USER->id); - $users = $DB->get_records(user_helper::TABLE); + $users = $DB->get_records(user_helper::LB_PLANNER_USER_TABLE); - $result = array(); + $results = []; - foreach ($users as $user) { - $mdluser = user_helper::get_mdl_user_info($user->userid); - $result[] = array( - 'userid' => $user->userid, - 'username' => $mdluser->username, - 'firstname' => $mdluser->firstname, - 'lastname' => $mdluser->lastname, - 'profileimageurl' => $mdluser->profileimageurl, - 'vintage' => $mdluser->vintage, - ); + foreach ($users as $userdata) { + $user = user::from_db($userdata); + if ($vintage === null || $vintage == $user->get_mdluser()->vintage) { + array_push($results, $user->prepare_for_api_short()); + } } - return $result; + return $results; } - public static function get_all_users_returns() { + /** + * Returns the structure of the data returned by the get_all_users function + * @return external_multiple_structure + */ + public static function get_all_users_returns(): external_multiple_structure { return new external_multiple_structure( - new external_single_structure( - array( - 'userid' => new external_value(PARAM_INT, 'The id of the user'), - 'username' => new external_value(PARAM_TEXT, 'The username of the user'), - 'firstname' => new external_value(PARAM_TEXT, 'The firstname of the user'), - 'lastname' => new external_value(PARAM_TEXT, 'The lastname of the user'), - 'profileimageurl' => new external_value(PARAM_URL, 'The url of the profile image'), - 'vintage' => new external_value(PARAM_TEXT, 'The vintage of the user') - ) - ) + user::api_structure_short() ); } } diff --git a/lbplanner/services/user/get_user.php b/lbplanner/services/user/get_user.php index d6737c02..b9abded2 100644 --- a/lbplanner/services/user/get_user.php +++ b/lbplanner/services/user/get_user.php @@ -16,93 +16,99 @@ namespace local_lbplanner_services; -use block_recentlyaccesseditems\external; -use core_privacy\local\request\subsystem\plugin_provider; +use coding_exception; +use dml_exception; use external_api; use external_function_parameters; use external_single_structure; -use external_multiple_structure; use external_value; -use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\user_helper; +use core_user; +use moodle_exception; + +use local_lbplanner\helpers\{user_helper, plan_helper, notifications_helper}; +use local_lbplanner\enums\{PLAN_EK, PLAN_ACCESS_TYPE, NOTIF_TRIGGER}; +use local_lbplanner\model\user; /** * Get the data for a user. + * + * Get the data for a user. param userid (optional) gives back the user data with the given ID + * + * @package local_lbplanner + * @subpackage services_user + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class user_get_user extends external_api { - public static function get_user_parameters() { - return new external_function_parameters(array( + /** + * Parameters for get_user + * @return external_function_parameters + */ + public static function get_user_parameters(): external_function_parameters { + global $USER; + return new external_function_parameters([ 'userid' => new external_value( PARAM_INT, - 'The id of the user to get the data for', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED + 'The id of the user to get the data for. If not provided it will be inferred via the token', + VALUE_DEFAULT, + $USER->id, + NULL_NOT_ALLOWED, ), - )); + ]); } - public static function get_user($userid) { - global $USER; - self::validate_parameters(self::get_user_parameters(), array('userid' => $userid)); + /** + * Gives back the data of a user. + * Default: The user who calls this function + * @param int $userid gives back the data of the given user + * @throws coding_exception + * @throws dml_exception + * @throws moodle_exception + * @return array The data of the user + */ + public static function get_user(int $userid): array { + global $USER, $DB; - if (!user_helper::check_user_exists($userid)) { - throw new \moodle_exception('User does not exist'); - } + self::validate_parameters(self::get_user_parameters(), ['userid' => $userid]); - $user = user_helper::get_user($userid); + // Check if the user is allowed to get the data for this userid. + user_helper::assert_access($userid); - $mdluser = user_helper::get_mdl_user_info($user->userid); + // Checks if the user is enrolled in LB Planner. + if (!user_helper::check_user_exists($userid)) { + // Register user if not found. + $lbplanneruser = new user(0, $userid, 'default', 'en', 'none', 1); + $lbpid = $DB->insert_record(user_helper::LB_PLANNER_USER_TABLE, $lbplanneruser->prepare_for_db()); + $lbplanneruser->set_fresh($lbpid); - // Check if the user is allowed to get the data for this userid. - if ($userid == $USER->id) { + // Create empty plan for newly registered user. + $plan = new \stdClass(); + $plan->name = 'Plan for ' . $USER->username; + $plan->enableek = PLAN_EK::ENABLED; + $planid = $DB->insert_record(plan_helper::TABLE, $plan); + $lbplanneruser->set_planid($planid); + + // Set user as owner of new plan. + $planaccess = new \stdClass(); + $planaccess->userid = $userid; + $planaccess->accesstype = PLAN_ACCESS_TYPE::OWNER; + $planaccess->planid = $planid; + $DB->insert_record(plan_helper::ACCESS_TABLE, $planaccess); - return array( - 'userid' => $user->userid, - 'username' => $mdluser->username, - 'firstname' => $mdluser->firstname, - 'lastname' => $mdluser->lastname, - 'capabilities' => user_helper::get_user_capability_bitmask($userid), - 'theme' => $user->theme, - 'lang' => $user->language, - 'profileimageurl' => $mdluser->profileimageurl, - 'planid' => plan_helper::get_plan_id($userid), - 'colorblindness' => $user->colorblindness, - 'displaytaskcount' => $user->displaytaskcount, - 'vintage' => $mdluser->vintage, - ); + // Notify the FE that this user likely hasn't used LBP before. + notifications_helper::notify_user($userid, -1, NOTIF_TRIGGER::USER_REGISTERED); + } else { + $lbplanneruser = user_helper::get_user($userid); } - return array( - 'userid' => $user->userid, - 'username' => $user->username, - 'firstname' => $mdluser->firstname, - 'lastname' => $mdluser->lastname, - 'capabilities' => null, - 'theme' => null, - 'lang' => null, - 'profileimageurl' => $mdluser->profileimageurl, - 'planid' => null, - 'colorblindness' => null, - 'displaytaskcount' => null, - 'vintage' => $mdluser->vintage, - ); + + return $lbplanneruser->prepare_for_api(); } - public static function get_user_returns() { - return new external_single_structure( - array( - 'userid' => new external_value(PARAM_INT, 'The id of the user'), - 'username' => new external_value(PARAM_TEXT, 'The username of the user'), - 'firstname' => new external_value(PARAM_TEXT, 'The firstname of the user'), - 'lastname' => new external_value(PARAM_TEXT, 'The lastname of the user'), - 'theme' => new external_value(PARAM_TEXT, 'The theme the user has selected'), - 'lang' => new external_value(PARAM_TEXT, 'The language the user has selected'), - 'profileimageurl' => new external_value(PARAM_URL, 'The url of the profile image'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan the user is assigned to'), - 'colorblindness' => new external_value(PARAM_TEXT, 'The colorblindness of the user'), - 'displaytaskcount' => new external_value(PARAM_INT, 'The displaytaskcount of the user'), - 'capabilities' => new external_value(PARAM_INT, 'The capability'), - 'vintage' => new external_value(PARAM_TEXT, 'The vintage of the user') - ) - ); + + /** + * Returns the data of a user. + * @return external_single_structure + */ + public static function get_user_returns(): external_single_structure { + return user::api_structure(); } } diff --git a/lbplanner/services/user/register_user.php b/lbplanner/services/user/register_user.php deleted file mode 100644 index 59a3fbe1..00000000 --- a/lbplanner/services/user/register_user.php +++ /dev/null @@ -1,108 +0,0 @@ -. - -namespace local_lbplanner_services; - -use external_api; -use external_function_parameters; -use external_single_structure; -use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\plan_helper; -use local_lbplanner\helpers\notifications_helper; - -/** - * Register a new user in the lbplanner app. - */ -class user_register_user extends external_api { - public static function register_user_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user to register', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'lang' => new external_value(PARAM_TEXT, 'The language the user has selected', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'theme' => new external_value(PARAM_TEXT, 'The theme the user has selected', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - )); - } - - public static function register_user($userid, $lang, $theme) { - global $DB; - - self::validate_parameters( - self::register_user_parameters(), - array('userid' => $userid, 'lang' => $lang, 'theme' => $theme) - ); - - user_helper::assert_access($userid); - - if (user_helper::check_user_exists($userid)) { - throw new \moodle_exception('User already registered'); - } - - $user = new \stdClass(); - $user->userid = $userid; - $user->language = $lang; - $user->theme = $theme; - $user->colorblindness = "none"; - - $DB->insert_record(user_helper::TABLE, $user); - - $mdluser = user_helper::get_mdl_user_info($userid); - - $plan = new \stdClass(); - $plan->name = 'Plan for ' . $mdluser->firstname; - $plan->enableek = plan_helper::EK_DISABLED; - - $planid = $DB->insert_record(plan_helper::TABLE, $plan); - - $planaccess = new \stdClass(); - $planaccess->userid = $userid; - $planaccess->accesstype = plan_helper::ACCESS_TYPE_OWNER; - $planaccess->planid = $planid; - - $DB->insert_record(plan_helper::ACCESS_TABLE, $planaccess); - - notifications_helper::notify_user($userid, -1, notifications_helper::TRIGGER_USER_REGISTERED); - - return array( - 'userid' => $user->userid, - 'username' => $mdluser->username, - 'firstname' => $mdluser->firstname, - 'lastname' => $mdluser->lastname, - 'capabilities' => user_helper::get_user_capability_bitmask($userid), - 'theme' => $user->theme, - 'lang' => $user->language, - 'profileimageurl' => $mdluser->profileimageurl, - 'planid' => $planid, - 'colorblindness' => $user->colorblindness, - ); - } - - public static function register_user_returns() { - return new external_single_structure( - array( - 'userid' => new external_value(PARAM_INT, 'The id of the user'), - 'username' => new external_value(PARAM_TEXT, 'The username of the user'), - 'firstname' => new external_value(PARAM_TEXT, 'The firstname of the user'), - 'lastname' => new external_value(PARAM_TEXT, 'The lastname of the user'), - 'capabilities' => new external_value(PARAM_INT, 'The capability'), - 'theme' => new external_value(PARAM_TEXT, 'The theme the user has selected'), - 'lang' => new external_value(PARAM_TEXT, 'The language the user has selected'), - 'profileimageurl' => new external_value(PARAM_URL, 'The url of the profile image'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan the user is assigned to'), - 'colorblindness' => new external_value(PARAM_TEXT, 'The colorblindness of the user'), - ) - ); - } -} diff --git a/lbplanner/services/user/update_user.php b/lbplanner/services/user/update_user.php index 9b681f46..cb9247e6 100644 --- a/lbplanner/services/user/update_user.php +++ b/lbplanner/services/user/update_user.php @@ -16,103 +16,99 @@ namespace local_lbplanner_services; +use dml_exception; use external_api; use external_function_parameters; use external_single_structure; use external_value; -use local_lbplanner\helpers\user_helper; -use local_lbplanner\helpers\plan_helper; +use invalid_parameter_exception; +use moodle_exception; + +use local_lbplanner\helpers\{plan_helper, user_helper}; +use local_lbplanner\model\user; /** * Update the data for a user. + * + * @package local_lbplanner + * @subpackage services_user + * @copyright 2024 necodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class user_update_user extends external_api { - public static function update_user_parameters() { - return new external_function_parameters(array( - 'userid' => new external_value(PARAM_INT, 'The id of the user to register', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'lang' => new external_value(PARAM_TEXT, 'The language the user has selected', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), - 'theme' => new external_value(PARAM_TEXT, 'The theme the user has selected', VALUE_REQUIRED, null, NULL_NOT_ALLOWED), + /** + * Parameters for update_user + * @return external_function_parameters + */ + public static function update_user_parameters(): external_function_parameters { + return new external_function_parameters([ + 'lang' => new external_value(PARAM_TEXT, 'The language the user has selected', VALUE_DEFAULT, null), + 'theme' => new external_value(PARAM_TEXT, 'The theme the user has selected', VALUE_DEFAULT, null), 'colorblindness' => new external_value( PARAM_TEXT, 'The colorblindness the user has selected', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED), + VALUE_DEFAULT, + null), 'displaytaskcount' => new external_value( PARAM_INT, - 'The displaytaskcount the user has selected', - VALUE_REQUIRED, - null, - NULL_NOT_ALLOWED), - )); + 'If the user has the taskcount-enabled 1-yes 0-no', + VALUE_DEFAULT, + null), + ]); } - public static function update_user($userid, $lang, $theme, $colorblindness, $displaytaskcount) { - global $DB; + /** + * Updates the given user in the lbplanner DB + * @param string $lang language the user choose + * @param string $theme The theme the user has selected + * @param string $colorblindness The colorblindness the user has selected + * @param int $displaytaskcount The displaytaskcount the user has selected + * @return array The updated user + * @throws moodle_exception + * @throws dml_exception + * @throws invalid_parameter_exception + */ + public static function update_user($lang, $theme, $colorblindness, $displaytaskcount): array { + global $DB, $USER; self::validate_parameters( self::update_user_parameters(), - array( - 'userid' => $userid, + [ 'lang' => $lang, 'theme' => $theme, 'colorblindness' => $colorblindness, - 'displaytaskcount' => $displaytaskcount - ) + 'displaytaskcount' => $displaytaskcount, + ] ); - - user_helper::assert_access($userid); - - if (!user_helper::check_user_exists($userid)) { - throw new \moodle_exception('User does not exist'); - } + user_helper::assert_access($USER->id); // Look if User-Id is in the DB. + if (!user_helper::check_user_exists($USER->id)) { + throw new moodle_exception('User does not exist'); + } + $user = user_helper::get_user($USER->id); + if ($lang !== null) { + $user->set_lang($lang); + } + if ($colorblindness !== null) { + $user->set_colorblindness($colorblindness); + } + if ($theme !== null) { + $user->set_theme($theme); + } + if ($displaytaskcount !== null) { + $user->set_displaytaskcount($displaytaskcount); + } - $user = user_helper::get_user($userid); - - $user->language = $lang; - $user->theme = $theme; - $user->colorblindness = $colorblindness; - $user->displaytaskcount = $displaytaskcount; - - $DB->update_record(user_helper::TABLE, $user, false); - - $mdluser = user_helper::get_mdl_user_info($userid); - - return array( - 'userid' => $userid, - 'lang' => $lang, - 'theme' => $theme, - 'capabilities' => user_helper::get_user_capability_bitmask($userid), - 'username' => $mdluser->username, - 'firstname' => $mdluser->firstname, - 'lastname' => $mdluser->lastname, - 'profileimageurl' => $mdluser->profileimageurl, - 'planid' => plan_helper::get_plan_id($userid), - 'colorblindness' => $colorblindness, - 'displaytaskcount' => $displaytaskcount, - 'vintage' => $mdluser->vintage, + $DB->update_record(user_helper::LB_PLANNER_USER_TABLE, $user->prepare_for_db()); - ); + return $user->prepare_for_api(); } - - public static function update_user_returns() { - return new external_single_structure( - array( - 'userid' => new external_value(PARAM_INT, 'The id of the user'), - 'username' => new external_value(PARAM_TEXT, 'The username of the user'), - 'firstname' => new external_value(PARAM_TEXT, 'The firstname of the user'), - 'lastname' => new external_value(PARAM_TEXT, 'The lastname of the user'), - 'capabilities' => new external_value(PARAM_INT, 'The role of the user'), - 'theme' => new external_value(PARAM_TEXT, 'The theme the user has selected'), - 'lang' => new external_value(PARAM_TEXT, 'The language the user has selected'), - 'profileimageurl' => new external_value(PARAM_URL, 'The url of the profile image'), - 'planid' => new external_value(PARAM_INT, 'The id of the plan the user is assigned to'), - 'colorblindness' => new external_value(PARAM_TEXT, 'The colorblindness the user has selected'), - 'displaytaskcount' => new external_value(PARAM_INT, 'The displaytaskcount the user has selected'), - 'vintage' => new external_value(PARAM_TEXT, 'The vintage of the user') - ) - ); + /** + * Returns the data of a user. + * @return external_single_structure + */ + public static function update_user_returns(): external_single_structure { + return user::api_structure(); } } diff --git a/lbplanner/settings.php b/lbplanner/settings.php index 24b59fd4..cccad430 100644 --- a/lbplanner/settings.php +++ b/lbplanner/settings.php @@ -14,6 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Defines some settings + * + * @package local_lbplanner + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + defined('MOODLE_INTERNAL') || die; if ($hassiteconfig) { diff --git a/lbplanner/version.php b/lbplanner/version.php index 68f4a001..69c161af 100644 --- a/lbplanner/version.php +++ b/lbplanner/version.php @@ -14,12 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +/** + * Defines versioning + * + * @package local_lbplanner + * @copyright 2024 NecodeIT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + defined('MOODLE_INTERNAL') || die(); $release = '0.0.0'; $plugin->component = 'local_lbplanner'; $plugin->release = 'Alpha v.'.$release; -$plugin->version = 2023030601; +$plugin->version = 2024031200; +$plugin->dependencies = [ + // Depend upon version 2023110600 of local_modcustomfields. + 'local_modcustomfields' => 2023110600, +]; set_config('release', $release, 'local_lbplanner');