Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,41 @@ The BIIGLE manual contains [some tutorials](https://biigle-admin-documentation.r

## How to use this template

First, [create a new repository](https://github.com/biigle/module/generate) based on this template. Then update the name of this module from `biigle/module` to whatever name you want to use for your module. The name has to be updated at the following locations:
First, [create a new repository](https://github.com/biigle/module/generate) (either private or public) based on this template.

1. [`QuotesController.php`](src/Http/Controllers/QuotesController.php#L16)
2. [`index.blade.php#L13`](src/resources/views/index.blade.php#L13)
3. [`index.blade.php#L16`](src/resources/views/index.blade.php#L16)
4. [`ModuleServiceProvider.php#L21`](src/ModuleServiceProvider.php#L21)
5. [`ModuleServiceProvider.php#L30`](src/ModuleServiceProvider.php#L30)
6. [`ModuleServiceProvider.php#L43`](src/ModuleServiceProvider.php#L43)
7. [`composer.json#L2`](composer.json#L2)
8. [`test.yml#L15`](.github/workflows/test.yml#L15)
## Development & Development Installation

Next, update the namespace of all PHP classes (`Biigle\Modules\Module`) and replace `Module` with the name of your module. Do this in [`webpack.mix.js#L23`](webpack.mix.js#L23), too. Also update this readme for your new module. You should remove the first two subsections and update the installation instructions. Now you can install the module and start developing.
Clone the module to a local directory. The name of this module needs to be updated in several locations. For your convenience, we provide a Python script to automate this process.

In addition to the code of the [tutorials](https://biigle.de/manual#developer-tutorials) this repository already contains the configuration for [Laravel Mix](https://laravel.com/docs/9.x/mix) as build system. To install the build system, run and then run `npm install`. Now you can use the following commands:
Run `python changeModuleName.py <your_module_name>` to update all locations at once.

- `npm run dev`: Starts the development server supporting hot module replacement.
- `npm run build`: Builds, minifies and publishes the assets once. Always do this before you commit new code.
If your module is not (yet) published on Packagist, you need to add the following to biigle's composer.json (found in the BIIGLE root directory; if repositories already exist, add the new one to the list):

```
"repositories": [
{
"type": "vcs",
"url": "git@github.com:<github_username>/<your_module_name>"
}
]
```

If the repository is private you need to have ssh-keys configured for your GitHub account. Run `composer require <github_username>/<your_module_name>:dev-main --ignore-platform-req=ext-ffi` in the BIIGLE root directory to install the module to BIIGLE. Still in the BIIGLE root directory run `php artisan vendor:publish --tag=public`.

The module is now installed to `biigle/vendor/<github_username>/<your_module_name>`. You can go there and develop your module.

- `npm run dev`: Continuously builds the assets during development.
- `npm run build`: Builds, minifies and publishes the assets once.
- `npm run lint`: Run static analysis to check for errors.

Also update this readme for your new module. You should remove the first three subsections and update the installation instructions. And don't forget to change the author and GitHub address in `composer.json` as well.

## Installation

Note that you have to replace `biigle/module` with the actual name of your module/repository.

1. Run `composer require biigle/module`. *This requires your module to be published on [Packagist](https://packagist.org/). If you don't want to publish your package, read more on [alternative options](https://getcomposer.org/doc/05-repositories.md#vcs).*
2. Add `Biigle\Modules\Module\ModuleServiceProvider::class` to the `providers` array in `config/app.php`. *Replace `Module` in the class namespace with the name of your module.*
3. Run `php artisan vendor:publish --tag=public` to refresh the public assets of the modules. Do this for every update of this module.
2. Run `php artisan vendor:publish --tag=public` to refresh the public assets of the modules. Do this for every update of this module.

## Developing

Expand Down
191 changes: 191 additions & 0 deletions changeModuleName.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#!/usr/bin/env python3

import argparse
from pathlib import Path


def replace_text(path: Path, old: str, new: str, expected_count: int = 1) -> bool:
content = path.read_text()
current_count = content.count(old)

if current_count == 0:
if new in content:
return False
raise ValueError(f"Did not find expected text in {path}: {old}")

if current_count != expected_count:
raise ValueError(
f"Expected {expected_count} occurrence(s) in {path}, found {current_count}: {old}"
)

updated = content.replace(old, new)
path.write_text(updated)
return True


def replace_namespace_prefix_in_php_files(root: Path, module: str) -> set[Path]:
old_prefix = "Biigle\\Modules\\Module"
new_prefix = f"Biigle\\Modules\\{module}"
changed_files: set[Path] = set()

for path in root.rglob("*.php"):
content = path.read_text()
if old_prefix not in content:
continue

path.write_text(content.replace(old_prefix, new_prefix))
changed_files.add(path)

return changed_files


def replace_text_in_php_files(root: Path, old: str, new: str) -> set[Path]:
changed_files: set[Path] = set()

for path in root.rglob("*.php"):
content = path.read_text()
if old not in content:
continue

path.write_text(content.replace(old, new))
changed_files.add(path)

return changed_files


def rename_service_provider_file(root: Path, module: str) -> tuple[Path, set[Path]]:
old_path = root / "src/ModuleServiceProvider.php"
new_path = root / f"src/{module.capitalize()}ServiceProvider.php"
changed_files: set[Path] = set()

if old_path.exists() and new_path.exists():
raise ValueError(
f"Both service provider files exist: {old_path} and {new_path}. Please resolve this manually."
)

if old_path.exists():
old_path.rename(new_path)
changed_files.add(new_path)

if not new_path.exists():
raise ValueError(
f"Service provider file not found. Expected one of: {old_path} or {new_path}"
)

return new_path, changed_files


def rename_service_provider_test_file(root: Path, module: str) -> set[Path]:
old_path = root / "tests/ModuleServiceProviderTest.php"
new_path = root / f"tests/{module}ServiceProviderTest.php"
changed_files: set[Path] = set()

if old_path.exists() and new_path.exists():
raise ValueError(
f"Both service provider test files exist: {old_path} and {new_path}. Please resolve this manually."
)

if old_path.exists():
old_path.rename(new_path)
changed_files.add(new_path)

if not new_path.exists():
raise ValueError(
f"Service provider test file not found. Expected one of: {old_path} or {new_path}"
)

return changed_files


def main() -> None:
parser = argparse.ArgumentParser(description="Update the module name in all relevant files.")
parser.add_argument("name", help="Module name")
args = parser.parse_args()

root = Path(__file__).resolve().parent
module = args.name
module_lower = module.lower()
provider_class = f"{module.capitalize()}ServiceProvider"
service_provider_path, renamed_files = rename_service_provider_file(root, module)
renamed_files.update(rename_service_provider_test_file(root, module))

replacements = [
(
root / "src/Http/Controllers/QuotesController.php",
"view('module::index')",
f"view('{module}::index')",
1,
),
(
root / "src/resources/views/index.blade.php",
"vendor/biigle/module/hot",
f"vendor/biigle/{module}/hot",
2,
),
(
service_provider_path,
"loadViewsFrom(__DIR__.'/resources/views', 'module');",
f"loadViewsFrom(__DIR__.'/resources/views', '{module}');",
1,
),
(
service_provider_path,
"$modules->register('module', [",
f"$modules->register('{module}', [",
1,
),
(
service_provider_path,
"public_path('vendor/module')",
f"public_path('vendor/{module}')",
1,
),
(
root / "composer.json",
'"name": "biigle/module"',
f'"name": "biigle/{module_lower}"',
1,
),
(
root / "composer.json",
'"Biigle\\\\Modules\\\\Module\\\\": "src"',
f'"Biigle\\\\Modules\\\\{module}\\\\": "src"',
1,
),
(
root / "composer.json",
'"Biigle\\\\Modules\\\\Module\\\\ModuleServiceProvider"',
f'"Biigle\\\\Modules\\\\{module}\\\\{provider_class}"',
1,
),
(
root / ".github/workflows/test.yml",
"MODULE_NAME: Module",
f"MODULE_NAME: {module}",
1,
),
]

changed_files = set(renamed_files)
for path, old, new, expected_count in replacements:
changed = replace_text(path, old, new, expected_count)
if changed:
changed_files.add(path)

changed_files.update(replace_namespace_prefix_in_php_files(root, module))
changed_files.update(
replace_text_in_php_files(root, "ModuleServiceProvider", provider_class)
)
if replace_text(
root / "package.json", "ModuleServiceProvider", provider_class, expected_count=1
):
changed_files.add(root / "package.json")

if changed_files:
print(f"Updated module name to '{module}' in {len(changed_files)} file(s).")
else:
print(f"No changes needed. Module name already set to '{module}'.")


if __name__ == "__main__":
main()
7 changes: 7 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,12 @@
"psr-4": {
"Biigle\\Modules\\Module\\": "src"
}
},
"extra": {
"laravel": {
"providers": [
"Biigle\\Modules\\Module\\ModuleServiceProvider"
]
}
}
}