From 106c81c4e0c82a946fb90841e9dda03758538a89 Mon Sep 17 00:00:00 2001 From: DursunCanPoyraz Date: Thu, 27 May 2021 21:01:40 +0300 Subject: [PATCH 1/2] tr-1.0.0-dev-0.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit composer.md update.md extensions.md console.md dosyaları güncellenecektir. --- docs/tr/composer.md | 164 +++++ docs/tr/console.md | 22 +- docs/tr/extend/README.md | 43 +- docs/tr/extend/admin.md | 215 ++++++- docs/tr/extend/api-throttling.md | 62 +- docs/tr/extend/api.md | 331 ++++++++++ docs/tr/extend/assets.md | 8 + docs/tr/extend/authorization.md | 207 +++++- docs/tr/extend/backend-events.md | 171 ++++- docs/tr/extend/console.md | 62 +- docs/tr/extend/distribution.md | 42 +- docs/tr/extend/extending-extensions.md | 125 ++++ docs/tr/extend/extensibility.md | 68 ++ docs/tr/extend/filesystem.md | 132 ++++ docs/tr/extend/formatting.md | 43 +- docs/tr/extend/forms.md | 115 +++- docs/tr/extend/frontend-pages.md | 226 ++++++- docs/tr/extend/frontend.md | 400 +++++++++++- docs/tr/extend/i18n.md | 474 +++++++++++++- docs/tr/extend/interactive-components.md | 89 ++- docs/tr/extend/language-packs.md | 59 ++ docs/tr/extend/mail.md | 60 +- docs/tr/extend/middleware.md | 157 ++++- docs/tr/extend/model-visibility.md | 271 ++++++++ docs/tr/extend/models.md | 311 +++++++++ docs/tr/extend/notifications.md | 418 +++++++++++- docs/tr/extend/permissions.md | 75 ++- docs/tr/extend/post-types.md | 4 +- docs/tr/extend/routes.md | 255 +++++++- docs/tr/extend/search.md | 214 ++++++- docs/tr/extend/service-provider.md | 69 +- docs/tr/extend/settings.md | 94 ++- docs/tr/extend/slugging.md | 4 +- docs/tr/extend/start.md | 159 ++++- docs/tr/extend/testing.md | 371 ++++++++++- docs/tr/extend/theme.md | 29 +- docs/tr/extend/update-1.0.md | 261 ++++++++ docs/tr/extend/update-b10.md | 54 +- docs/tr/extend/update-b12.md | 36 +- docs/tr/extend/update-b13.md | 41 +- docs/tr/extend/update-b14.md | 778 ++++++++++++++++++++++- docs/tr/extend/update-b15.md | 61 +- docs/tr/extend/update-b16.md | 141 ++++ docs/tr/extend/update-b8.md | 115 +++- docs/tr/extenders.md | 25 + docs/tr/extensions.md | 12 + docs/tr/install.md | 12 +- docs/tr/troubleshoot.md | 10 +- docs/tr/update.md | 101 ++- 49 files changed, 7112 insertions(+), 84 deletions(-) create mode 100644 docs/tr/composer.md create mode 100644 docs/tr/extend/api.md create mode 100644 docs/tr/extend/assets.md create mode 100644 docs/tr/extend/extending-extensions.md create mode 100644 docs/tr/extend/extensibility.md create mode 100644 docs/tr/extend/filesystem.md create mode 100644 docs/tr/extend/language-packs.md create mode 100644 docs/tr/extend/model-visibility.md create mode 100644 docs/tr/extend/models.md create mode 100644 docs/tr/extend/update-1.0.md create mode 100644 docs/tr/extend/update-b16.md create mode 100644 docs/tr/extenders.md diff --git a/docs/tr/composer.md b/docs/tr/composer.md new file mode 100644 index 000000000..bfc4aba21 --- /dev/null +++ b/docs/tr/composer.md @@ -0,0 +1,164 @@ + +# Composer + +Flarum uses a program called composer[Composer](https://getcomposer.org) to manage its dependencies and extensions. +You'll need to use composer if you want to: + +- Install or update Flarum +- Install, update, or remove Flarum extensions + +This guide is provided as a brief explanation of Composer. We highly recommend consulting the [official documentation](https://getcomposer.org/doc/00-intro.md) for more information. + +::: tip Composer v2 + +Historically, Composer has caused issues on shared hosting due to huge memory use. In 2020, [Composer v2 was released](https://blog.packagist.com/composer-2-0-is-now-available/) with massive performance and memory usage improvements that eliminate these problems. Make sure your server is using Composer v2! + +::: + +## What is Composer? + +> Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you. — [Composer Introduction]([https://getcomposer.org/doc/00-intro.md](https://getcomposer.org/doc/00-intro.md)) + +Each Flarum installation consists primarily of Flarum core and a set of [extensions](extensions.md).Each of these has its own dependencies and releases. + +Back in the day, forum frameworks would manage extensions by having users upload zip files with the extension code. That seems simple enough, but issues quickly become evident: + +- Uploading random zip files from the internet tends to be a bad idea. Requiring that extensions be downloaded from a central source like [Packagist](https://packagist.org/) makes it somewhat more tedious to spam malicious code, and ensures that source code is available on GitHub for free/public extensions. +- Let's say Extension A requires v4 of some library, and Extension B requires v5 of that same library. With a zip-based solution, either one of the two dependencies could override the other, causing all sorts of inconsistent problems. Or both would attempt to run at once, which would cause PHP to crash (you can't declare the same class twice). +- Zip files can cause a lot of headache if trying to automate deployments, run automated tests, or scale to multiple server nodes. +- There is no good way to ensure conflicting extension versions can't be installed, or that system PHP version and extension requirements are met. +- Sure, we can upgrade extensions by replacing the zip file. But what about upgrading Flarum core? And how can we ensure that extensions can declare which versions of core they're compatible with? + +Composer takes care of all these issues, and more! + +## Flarum and Composer + +When you go to [install Flarum](install.md#installing), you're actually doing 2 things: + +1. Downloading a boilerplate "skeleton" for Flarum. This includes an `index.php` file that handles web requests, a `flarum` file that provides a CLI, and a bunch of web server config and folder setup. This is taken from the [`flarum/flarum` github repository](https://github.com/flarum/flarum), and doesn't actually contain any of the code necessary for Flarum to run. +2. Installing `composer` packages necessary for Flarum, namely Flarum core, and several bundled extensions. These are called by the `index.php` and `flarum` files from step 1, and are the implementation of Flarum. These are specified in a `composer.json` file included in the skeleton. + +When you want to update Flarum or add/update/remove extensions, you'll do so by running `composer` commands. Each command is different, but all commands follow the same general process: + +1. Update the `composer.json` file to add/remove/update the package. +2. Do a bunch of math to get the latest compatible versions of everything if possible, or figure out why the requested arrangement is impossible. +3. If everything works, download new versions of everything that needs to be updated. If not, revert the `composer.json` changes + +When running `composer.json` commands, make sure to pay attention to the output. If there's an error, it'll probably tell you if it's because of extension incompatibilities, an unsupported PHP version, missing PHP extensions, or something else. + +### The `composer.json` File + +As mentioned above, the entire composer configuration for your Flarum site is contained inside the `composer.json` file. You can consult the [composer documentation](https://getcomposer.org/doc/04-schema.md) for a specific schema, but for now, let's go over an annotated `composer.json` from `flarum/flarum`: + +```jsonc +{ + // This following section is mostly just metadata about the package. + // For forum admins, this doesn't really matter. + "name": "flarum/flarum", + "description": "Delightfully simple forum software.", + "type": "project", + "keywords": [ + "forum", + "discussion" + ], + "homepage": "https://flarum.org/", + "license": "MIT", + "authors": [ + { + "name": "Flarum", + "email": "info@flarum.org", + "homepage": "https://flarum.org/team" + } + ], + "support": { + "issues": "https://github.com/flarum/core/issues", + "source": "https://github.com/flarum/flarum", + "docs": "https://flarum.org/docs/" + }, + // End of metadata + + // This next section is the one we care about the most. + // It's a list of packages we want, and the versions for each. + // We'll discuss this shortly. + "require": { + "flarum/core": "^0.1.0", + "flarum/approval": "^0.1.0", + "flarum/bbcode": "^0.1.0", + "flarum/emoji": "^0.1.0", + "flarum/lang-english": "^0.1.0", + "flarum/flags": "^0.1.0", + "flarum/likes": "^0.1.0", + "flarum/lock": "^0.1.0", + "flarum/markdown": "^0.1.0", + "flarum/mentions": "^0.1.0", + "flarum/nicknames": "^0.1.0", + "flarum/pusher": "^0.1.0", + "flarum/statistics": "^0.1.0", + "flarum/sticky": "^0.1.0", + "flarum/subscriptions": "^0.1.0", + "flarum/suspend": "^0.1.0", + "flarum/tags": "^0.1.0" + }, + + // Various composer config. The ones here are sensible defaults. + // See https://getcomposer.org/doc/06-config.md for a list of options. + "config": { + "preferred-install": "dist", + "sort-packages": true + }, + + // If composer can find a stable (not dev, alpha, or beta) version + // of a package, it should use that. Generally speaking, production + // sites shouldn't run beta software unless you know what you're doing. + "prefer-stable": true +} +``` + +Let's focus on that `require` section. Each entry is the name of a composer package, and a version string. +To read more about version strings, see the relevant [composer documentation](https://semver.org/). + +For Flarum projects, there's several types of entries you'll see in the `require` section of your root install's `flarum/core`: + +- You MUST have a `flarum/core` entry. This should have an explicit version string corresponding to the major release you want to install. For Flarum 1.x versions, this would be `^1.0.0`. +- You should have an entry for each extension you've installed. Some bundled extensions are included by default (e.g. `flarum/tags`, `flarum/suspend`, etc), [others you'll add via composer commands](extensions.md). Unless you have a reason to do otherwise (e.g. you're testing a beta version of a package), we recommend using an asterisk as the version string for extensions (`*`). This means "install the latest version compatible with my flarum/core". +- Some extensions / features might require PHP packages that aren't Flarum extensions. For example, you need the guzzle library to use the [Mailgun mail driver](mail.md). In these cases, the instructions for the extension/feature in question should explain which version string to use. + +## How to install Composer? + +As with any other software, Composer must first be [installed]([https://getcomposer.org/download/) on the server where Flarum is running. There are several options depending on the type of web hosting you have. + +### Dedicated Web Server + +In this case you can install composer as recommended in the Composer [guide]([https://getcomposer.org/doc/00-intro.md#system-requirements) + +### Managed / Shared hosting + +If Composer is not preinstalled (you can check this by running `composer --version`), you can use a [manual installation](https://getcomposer.org/composer-stable.phar). Just upload the composer.phar to your folder and run `/path/to/your/php7 composer.phar COMMAND` for any command documented as `composer COMMAND`. + +::: danger + +Some articles on the internet will mention that you can use tools like a PHP shell. If you are not sure what you are doing or what they are talking about - be careful! An unprotected web shell is **extremely** dangerous. + +::: + +## How do I use Composer? + +You'll need to use Composer over the **C**ommand-**l**ine **i**nterface (CLI). Be sure you can access your server over **S**ecure **Sh**ell (SSH). + +Once you have Composer installed, you should be able to run Composer commands in your SSH terminal via `composer COMMAND`. + +::: Optimizations + +After most commands, you'll want to run `composer dump-autoload -a`. Essentially, this caches PHP files so they run faster. + +::: + +## I don't have SSH access + +Most decent hosts should provide SSH access for shared hosting. If your host doesn't (and you can't switch to a good host that does offer it), hope might not yet be lost. You have several options: + +- Use alternatives like [Pockethold](https://github.com/UvUno/pockethold) to install Flarum. Note that you'll still need composer (and SSH) to install extensions. +- Install composer on your computer, and run the `install` command locally. Then upload the files via FTP to your host. To make modifications (updating Flarum, installing/updating/removing extensions), download the current versions of the files, run whatever composer commands you need locally, and then replace the `composer.json` and `composer.lock` files, and the `vendor` directory of your install with your local copy. Make sure to create backups before doing this! +- Some web hosts might provide a GUI for managing composer. The command line version is generally preferably, but if a GUI is the only possibility, consult your host's documentation for information on how to use it. + +Note that these workarounds are not officially supported! The only officially supported way to install and manage Flarum is through Composer. diff --git a/docs/tr/console.md b/docs/tr/console.md index ea33eb8ec..66f619ec3 100644 --- a/docs/tr/console.md +++ b/docs/tr/console.md @@ -48,4 +48,24 @@ Bekleyen tüm geçişleri çalıştırır. Bu, veritabanını değiştiren bir u `php flarum migrate:reset --extension [extension_id]` -Bir uzantı için tüm geçişleri sıfırlayın. Bu, çoğunlukla uzantı geliştiricileri tarafından kullanılır, ancak bazen, bir uzantıyı kaldırıyorsanız ve tüm verilerini veritabanından temizlemek istiyorsanız bunu çalıştırmanız gerekebilir. Lütfen bunun çalışması için söz konusu uzantının şu anda yüklü olması ancak mutlaka etkinleştirilmesi gerekmediğini unutmayın. \ No newline at end of file +Bir uzantı için tüm geçişleri sıfırlayın. Bu, çoğunlukla uzantı geliştiricileri tarafından kullanılır, ancak bazen, bir uzantıyı kaldırıyorsanız ve tüm verilerini veritabanından temizlemek istiyorsanız bunu çalıştırmanız gerekebilir. Lütfen bunun çalışması için söz konusu uzantının şu anda yüklü olması ancak mutlaka etkinleştirilmesi gerekmediğini unutmayın. + +### schedule:run + +`php flarum schedule:run` + +Many extensions use scheduled jobs to run tasks on a regular interval. This could include database cleanups, posting scheduled drafts, generating sitemaps, etc. If any of your extensions use scheduled jobs, you should add a [cron job](https://ostechnix.com/a-beginners-guide-to-cron-jobs/) to run this command on a regular interval: + +``` +* * * * * cd /path-to-your-flarum-install && php flarum schedule:run >> /dev/null 2>&1 +``` + +This command should generally not be run manually. + +Note that some hosts do not allow you to edit cron configuration directly. In this case, you should consult your host for more information on how to schedule cron jobs. + +### schedule:list + +`php flarum schedule:list` + +This command returns a list of scheduled commands (see `schedule:run` for more information). This is useful for confirming that commands provided by your extensions are registered properly. This **can not** check that cron jobs have been scheduled successfully, or are being run. \ No newline at end of file diff --git a/docs/tr/extend/README.md b/docs/tr/extend/README.md index 8041fcd5c..57d53f7a5 100644 --- a/docs/tr/extend/README.md +++ b/docs/tr/extend/README.md @@ -1,3 +1,42 @@ -### Flarum Genişletme +# Extending Flarum -Yakında Türkçe belgeler eklenecektir. \ No newline at end of file +Flarum is minimalistic, but it's also highly extensible. In fact, most of the features that ship with Flarum are actually extensions! + +This approach makes Flarum extremely customizable. A user can disable any features they don't use on their forum, and install other extensions to make a forum perfect for their community. + +In order to achieve this extensibility, Flarum has been built with rich APIs and extension points. With some programming knowledge, you can leverage these APIs to add just about any feature you want. This section of the documentation aims to teach you how Flarum works, and how to use the APIs so that you can build your own extensions. + +::: warning + +**Both the Extension API and this documentation is a work in progress.** Be aware that future beta releases may break your extensions! If you have feedback, [we'd love to hear it](https://discuss.flarum.org/). + +::: + +## Core vs. Extensions + +Where do we draw the line between Flarum's core and its extensions? Why are some features included in the core, and others aren't? It is important to understand this distinction so that we can maintain consistency and quality within Flarum's ecosystem. + +**Flarum's core** is not intended to be packed full of features. Rather, it is a scaffold, or a framework, which provides a reliable foundation on which extensions can build. It contains only basic, unopinionated functionality that is essential to a forum: discussions, posts, users, groups, and notifications. + +**Bundled extensions** are features that are packaged with Flarum and enabled by default. They are extensions just like any other, and may be disabled and uninstalled. While their scope is not intended to address all use-cases, the idea is to make them generic and configurable enough that they can satisfy the majority. + +**Third-party extensions** are features which are made by others and are not officially supported by the Flarum team. They can be built and used to address more specific use-cases. + +If you are aiming to address a bug or shortcoming of the core, or of an existing bundled extension, it may be appropriate to *contribute to the respective project* rather than disperse effort on a new third-party extension. It is a good idea to start a discussion on the [Flarum Community](https://discuss.flarum.org/) to get the perspective of the Flarum developers. + +## Useful Resources + +- [This Documentation](start.md) +- [Tips for Beginning Developers](https://discuss.flarum.org/d/5512-extension-development-tips) +- [Developers explaining their workflow for extension development](https://discuss.flarum.org/d/6320-extension-developers-show-us-your-workflow) +- [Extension namespace tips](https://discuss.flarum.org/d/9625-flarum-extension-namespacing-tips) +- [Mithril js documentation](https://mithril.js.org/) +- [Laravel API Docs](https://laravel.com/api/8.x/) +- [Flarum API Docs](https://api.flarum.org) +- [ES6 cheatsheet](https://github.com/DrkSephy/es6-cheatsheet) +- [Flarum Blank Extension Generator](https://discuss.flarum.org/d/11333-flarum-extension-generator-by-reflar/) + +### Getting help + +- [Official Flarum Dev Community](https://discuss.flarum.org/t/dev) +- [Join us on #extend in our discord chat](https://flarum.org/discord/) diff --git a/docs/tr/extend/admin.md b/docs/tr/extend/admin.md index 5c2d63b6b..dd0bbfad1 100644 --- a/docs/tr/extend/admin.md +++ b/docs/tr/extend/admin.md @@ -1,3 +1,214 @@ -# Yönetici Gösterge Tablosu +# Admin Dashboard -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Beta 15 introduced a completely redesigned admin panel and frontend API. It is now easier than ever to add settings or permissions to your extension. + +Before beta 15, extension settings were either added in a `SettingsModal` or they added a new page for more complex settings. Now, every extension has a page containing info, settings, and the extension's own permissions. + +You can simply register settings, extend the base [`ExtensionPage`](https://api.docs.flarum.org/js/master/class/src/admin/components/extensionpage.js~extensionpage), or provide your own completely custom page. + +## Extension Data API + +This new API allows you to add settings to your extension with very few lines of code. + +### Telling the API about your extension + +Before you can register anything, you need to tell `ExtensionData` what extension it is about to get data for. + +Simply run the `for` function on `app.extensionData` passing in the id of your extension. To find you extension id, take the composer name and replace any slashes with dashes (example: 'fof/merge-discussions' becomes 'fof-merge-discussions'). Extensions with the `flarum-` and `flarum-ext-` will omit those from the name (example: 'webbinaro/flarum-calendar' becomes 'webbinaro-calendar'). + +For the following example, we will use the fictitious extension 'acme/interstellar': + +```js + +app.initializers.add('interstellar', function(app) { + + app.extensionData + .for('acme-interstellar') +}); +``` + +Once that is done, you can begin adding settings and permissions. + +:::tip Note + +All registration functions on `ExtensionData` are chainable, meaning you can call them one after another without running `for` again. + +::: + +### Registering Settings + +Adding settings fields in this way is recommended for simple items. As a rule of thumb, if you only need to store things in the settings table, this should be enough for you. + +To add a field, call the `registerSetting` function after `for` on `app.extensionData` and pass a 'setting object' as the first argument. Behind the scenes `ExtensionData` actually turns your settings into an [`ItemList`](https://api.docs.flarum.org/js/master/class/src/common/utils/itemlist.ts~itemlist), you can pass a priority number as the second argument. + +Here's an example with a switch (boolean) item: + +```js + +app.initializers.add('interstellar', function(app) { + + app.extensionData + .for('acme-interstellar') + .registerSetting( + { + setting: 'acme-interstellar.coordinates', // This is the key the settings will be saved under in the settings table in the database. + label: app.translator.trans('acme-interstellar.admin.coordinates_label'), // The label to be shown letting the admin know what the setting does. + help: app.translator.trans('acme-interstellar.admin.coordinates_help'), // Optional help text where a longer explanation of the setting can go. + type: 'boolean', // What type of setting this is, valid options are: boolean, text (or any other tag type), and select. + }, + 30 // Optional: Priority + ) +}); +``` + +If you use `type: 'select'` the setting object looks a little bit different: + +```js +{ + setting: 'acme-interstellar.fuel_type', + label: app.translator.trans('acme-interstellar.admin.fuel_type_label'), + type: 'select', + options: { + 'LOH': 'Liquid Fuel', // The key in this object is what the setting will be stored as in the database, the value is the label the admin will see (remember to use translations if they make sense in your context). + 'RDX': 'Solid Fuel', + }, + default: 'LOH', +} +``` + +Also, note that additional items in the setting object will be used as component attrs. This can be used for placeholders, min/max restrictions, etc: + +```js +{ + setting: 'acme-interstellar.crew_count', + label: app.translator.trans('acme-interstellar.admin.crew_count_label'), + type: 'number', + min: 1, + max: 10 +} +``` + +If you want to add something to the settings like some extra text or a more complicated input, you can also pass a callback as the first argument that returns JSX. This callback will be executed in the context of [`ExtensionPage`](https://api.docs.flarum.org/js/master/class/src/admin/components/extensionpage.js~extensionpage) and setting values will not be automatically serialized. + +```js + +app.initializers.add('interstellar', function(app) { + + app.extensionData + .for('acme-interstellar') + .registerSetting(function () { + if (app.session.user.username() === 'RocketMan') { + + return ( +
+

{app.translator.trans('acme-interstellar.admin.you_are_rocket_man_label')}

+ +
+ ); + } + }) +}); +``` + +### Registering Permissions + +New in beta 15, permissions can now be found in 2 places. Now, you can view each extension's individual permissions on their page. All permissions can still be found on the permissions page. + +In order for that to happen, permissions must be registered with `ExtensionData`. This is done in a similar way to settings, call `registerPermission`. + +Arguments: + * Permission object + * What type of permission - see [`PermissionGrid`](https://api.docs.flarum.org/js/master/class/src/admin/components/permissiongrid.js~permissiongrid)'s functions for types (remove items from the name) + * `ItemList` priority + +Back to our favorite rocket extension: + +```js +app.initializers.add('interstellar', function(app) { + + app.extensionData + .for('acme-interstellar') + .registerPermission( + { + icon: 'fas fa-rocket', // Font-Awesome Icon + label: app.translator.trans('acme-interstellar.admin.permissions.fly_rockets_label'), // Permission Label + permission: 'discussion.rocket_fly', // Actual permission name stored in database (and used when checking permission). + tagScoped: true, // Whether it be possible to apply this permission on tags, not just globally. Explained in the next paragraph. + }, + 'start', // Category permission will be added to on the grid + 95 // Optional: Priority + ); +}); +``` + +If your extension interacts with the [tags extension](https://github.com/flarum/tags) (which is fairly common), you might want a permission to be tag scopable (i.e. applied on the tag level, not just globally). You can do this by including a `tagScoped` attribute, as seen above. Permissions starting with `discussion.` will automatically be tag scoped unless `tagScoped: false` is indicated. + +To learn more about Flarum permissions, see [the relevant docs](permissions.md). + +### Chaining Reminder + +Remember these functions can all be chained like: + +```js +app.extensionData + .for('acme-interstellar') + .registerSetting(...) + .registerSetting(...) + .registerPermission(...) + .registerPermission(...); +``` + +### Extending/Overriding the Default Page + +Sometimes you have more complicated settings that mess with relationships, or just want the page to look completely different. In this case, you will need to tell `ExtensionData` that you want to provide your own page. Note that `buildSettingComponent`, the util used to register settings by providing a descriptive object, is available as a method on `ExtensionPage` (extending from `AdminPage`, which is a generic base for all admin pages with some util methods). + +Create a new class that extends the `Page` or `ExtensionPage` component: + +```js +import ExtensionPage from 'flarum/admin/components/ExtensionPage'; + +export default class StarPage extends ExtensionPage { + content() { + return ( +

Hello from the settings section!

+ ) + } +} + +``` + +Then, simply run `registerPage`: + +```js + +import StarPage from './components/StarPage'; + +app.initializers.add('interstellar', function(app) { + + app.extensionData + .for('acme-interstellar') + .registerPage(StarPage); +}); +``` + +This page will be shown instead of the default. + +You can extend the [`ExtensionPage`](https://api.docs.flarum.org/js/master/class/src/admin/components/extensionpage.js~extensionpage) or extend the base `Page` and design your own! + +## Composer.json Metadata + +In beta 15, extension pages make room for extra info which is pulled from extensions' composer.json. + +For more information, see the [composer.json schema](https://getcomposer.org/doc/04-schema.md). + +| Description | Where in composer.json | +| --------------------------------- | -------------------------------------- | +| discuss.flarum.org discussion link | "forum" key inside "support" | +| Documentation | "docs" key inside "support" | +| Support (email) | "email" key inside "support" | +| Website | "homepage" key | +| Donate | "funding" key block (Note: Only the first link will be used) | +| Source | "source" key inside "support" | diff --git a/docs/tr/extend/api-throttling.md b/docs/tr/extend/api-throttling.md index 27ef88b10..a1caa2650 100644 --- a/docs/tr/extend/api-throttling.md +++ b/docs/tr/extend/api-throttling.md @@ -1,3 +1,61 @@ -# API Azaltma +# API Throttling -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Flarum comes with a builtin `Flarum\Api\Middleware\ThrottleApi` [middleware](middleware.md) for throttling requests to the API. +This runs on every API route, and extensions can add their own custom logic to throttle requests. + +::: warning Forum Routes + +Some forum routes (login, register, forgot password, etc) work by calling an API route under the surface. +The `ThrottleApi` middleware does not currently run for these requests, but that is planned for the future. + +::: + +## Custom Throttlers + +The format for a custom throttler is extremely simple: all you need is a closure or invokable class that takes the current request as an argument, and returns one of: + +- `false`: This explicitly bypasses throttling for this request, overriding all other throttlers +- `true`: This marks the request as to be throttled. +- `null`: This means that this throttler doesn't apply. +Any other outputs will be ignored, with the same effect as `null`. + +Throttlers will be run on EVERY request, and are responsible for figuring out whether or not they apply. For example, consider Flarum's post throttler: + +```php +use DateTime; +use Flarum\Post\Post; + +function ($request) { + if (! in_array($request->getAttribute('routeName'), ['discussions.create', 'posts.create'])) { + return; + } + + $actor = $request->getAttribute('actor'); + + if ($actor->can('postWithoutThrottle')) { + return false; + } + + if (Post::where('user_id', $actor->id)->where('created_at', '>=', new DateTime('-10 seconds'))->exists()) { + return true; + } +}; +``` + +Throttlers can be added or removed via the `ThrottleApi` middleware in `extend.php`. For example: + +```php +set('throttleAll', function () { + return false; + }) + ->remove('bypassThrottlingAttribute'), + // Other extenders +]; +``` diff --git a/docs/tr/extend/api.md b/docs/tr/extend/api.md new file mode 100644 index 000000000..6b12f319a --- /dev/null +++ b/docs/tr/extend/api.md @@ -0,0 +1,331 @@ +# API and Data Flow + +In the [previous article](models.md), we learned how Flarum uses models to interact with data. Here, we'll learn how to get that data from the database to the JSON-API to the frontend, and all the way back again. + +## API Request Lifecycle + +Before we go into detail about how to extend Flarum's data API, it's worth thinking about the lifecycle of a typical API request: + +![Flarum API Flowchart](/en/api_flowchart.png) + +1. An HTTP request is sent to Flarum's API. Typically, this will come from the Flarum frontend, but external programs can also interact with the API. Flarum's API mostly follows the [JSON:API](https://jsonapi.org/) specification, so accordingly, requests should follow [said specification](https://jsonapi.org/format/#fetching). +2. The request is run through [middleware](middleware.md), and routed to the proper controller. You can learn more about controllers as a whole on our [routes and content documentation](routes.md). Assuming the request is to the API (which is the case for this section), the controller that handles the request will be a subclass of `Flarum\Api\AbstractSerializeController`. +3. Any modifications done by extensions to the controller via the [`ApiController` extender](#extending-api-controllers) are applied. This could entail changing sort, adding includes, changing the serializer, etc. +4. The `$this->data()` method of the controller is called, yielding some raw data that should be returned to the client. Typically, this data will take the form of a Laravel Eloquent model collection or instance, which has been retrieved from the database. That being said, the data could be anything as long as the controller's serializer can process it. Each controller is responsible for implementing its own `data` method. Note that for `PATCH`, `POST`, and `DELETE` requests, `data` will perform the operation in question, and return the modified model instance. +5. That data is run through any pre-serialization callbacks that extensions register via the [`ApiController` extender](#extending-api-controllers). +6. The data is passed through a [serializer](#serializers), which converts it from the backend, database-friendly format to the JSON:API format expected by the frontend. It also attaches any related objects, which are run through their own serializers. As we'll explain below, extensions can [add / override relationships and attributes](#attributes-and-relationships) at the serialization level. +7. The serialized data is returned as a JSON response to the frontend. +8. If the request originated via the Flarum frontend's `Store`, the returned data (including any related objects) will be stored as [frontend models](#frontend-models) in the frontend store. + +## API Endpoints + +We learned how to use models to interact with data, but we still need to get that data from the backend to the frontend. +We do this by writing API Controller [routes](routes.md), which implement logic for API endpoints. + +As per the JSON:API convention, we'll want to add separate endpoints for each operation we support. Common operations are: + +- Listing instances of a model (possibly including searching/filtering) +- Getting a single model instance +- Creating a model instance +- Updating a model instance +- Deleting a single model instance + +We'll go over each type of controler shortly, but once they're written, you can add these five standard endpoints (or a subset of them) using the `Routes` extender: + +```php + (new Extend\Routes('api')) + ->get('/tags', 'tags.index', ListTagsController::class) + ->get('/tags/{id}', 'tags.show', ShowTagController::class) + ->post('/tags', 'tags.create', CreateTagController::class) + ->patch('/tags/{id}', 'tags.update', UpdateTagController::class) + ->delete('/tags/{id}', 'tags.delete', DeleteTagController::class) +``` + +::: warning + +Paths to API endpoints are not arbitrary! To support interactions with frontend models: + +- The path should either be `/prefix/{id}` for get/update/delete, or `/prefix` for list/create. +- the prefix (`tags` in the example above) must correspond to the JSON:API model type. You'll also use this model type in your serializer's `$type` attribute, and when registering the frontend model (`app.store.models.TYPE = MODEL_CLASS`). +- The methods must match the example above. + +Also, remember that route names (`tags.index`, `tags.show`, etc) must be unique! + +::: + +The `Flarum\Api\Controller` namespace contains a number of abstract controller classes that you can extend to easily implement your JSON-API resources. + +### Listing Resources + +For the controller that lists your resource, extend the `Flarum\Api\Controller\AbstractListController` class. At a minimum, you need to specify the `$serializer` you want to use to serialize your models, and implement a `data` method to return a collection of models. The `data` method accepts the `Request` object and the tobscure/json-api `Document`. + +```php +use Flarum\Api\Controller\AbstractListController; +use Psr\Http\Message\ServerRequestInterface as Request; +use Tobscure\JsonApi\Document; + +class ListTagsController extends AbstractListController +{ + public $serializer = TagSerializer::class; + + protected function data(Request $request, Document $document) + { + return Tag::all(); + } +} +``` + +#### Pagination + +You can allow the number of resources being **listed** to be customized by specifying the `limit` and `maxLimit` properties on your controller: + +```php + // The number of records included by default. + public $limit = 20; + + // The maximum number of records that can be requested. + public $maxLimit = 50; +``` + +You can then extract pagination information from the request using the `extractLimit` and `extractOffset` methods: + +```php +$limit = $this->extractLimit($request); +$offset = $this->extractOffset($request); + +return Tag::skip($offset)->take($limit); +``` + +To add pagination links to the JSON:API document, use the `Document::addPaginationLinks` method. + +#### Sorting + +You can allow the sort order of resources being **listed** to be customized by specifying the `sort` and `sortField` properties on your controller: + +```php + // The default sort field and order to use. + public $sort = ['name' => 'asc']; + + // The fields that are available to be sorted by. + public $sortFields = ['firstName', 'lastName']; +``` + +You can then extract sorting information from the request using the `extractSort` method. This will return an array of sort criteria which you can apply to your query: + +```php +$sort = $this->extractSort($request); +$query = Tag::query(); + +foreach ($sort as $field => $order) { + $query->orderBy(snake_case($field), $order); +} + +return $query->get(); +``` + +#### Searching and Filtering + +Read our [searching and filtering](search.md) guide for more information! + +### Showing a Resource + +For the controller that shows a single resource, extend the `Flarum\Api\Controller\AbstractShowController` class. Like for the list controller, you need to specify the `$serializer` you want to use to serialize your models, and implement a `data` method to return a single model. We'll learn about serializers [in just a bit](#serializers). + +```php +use Flarum\Api\Controller\AbstractShowController; +use Illuminate\Support\Arr; +use Psr\Http\Message\ServerRequestInterface as Request; +use Tobscure\JsonApi\Document; + +class ShowTagController extends AbstractShowController +{ + public $serializer = TagSerializer::class; + + protected function data(Request $request, Document $document) + { + $id = Arr::get($request->getQueryParams(), 'id'); + + return Tag::findOrFail($id); + } +} +``` + +### Creating a Resource + +For the controller that creates a resource, extend the `Flarum\Api\Controller\AbstractCreateController` class. This is the same as the show controller, except the response status code will automatically be set to `201 Created`. You can access the incoming JSON:API document body via `$request->getParsedBody()`: + +```php +use Flarum\Api\Controller\AbstractCreateController; +use Illuminate\Support\Arr; +use Psr\Http\Message\ServerRequestInterface as Request; +use Tobscure\JsonApi\Document; + +class CreateTagController extends AbstractCreateController +{ + public $serializer = TagSerializer::class; + + protected function data(Request $request, Document $document) + { + $attributes = Arr::get($request->getParsedBody(), 'data.attributes'); + + return Tag::create([ + 'name' => Arr::get($attributes, 'name') + ]); + } +} +``` + +### Updating a Resource + +For the controller that updates a resource, extend the `Flarum\Api\Controller\AbstractShowController` class. Like for the create controller, you can access the incoming JSON:API document body via `$request->getParsedBody()`. + +### Deleting a Resource + +For the controller that deletes a resource, extend the `Flarum\Api\Controller\AbstractDeleteController` class. You only need to implement a `delete` method which enacts the deletion. The controller will automatically return an empty `204 No Content` response. + +```php +use Flarum\Api\Controller\AbstractDeleteController; +use Illuminate\Support\Arr; +use Psr\Http\Message\ServerRequestInterface as Request; + +class DeleteTagController extends AbstractDeleteController +{ + protected function delete(Request $request) + { + $id = Arr::get($request->getQueryParams(), 'id'); + + Tag::findOrFail($id)->delete(); + } +} +``` + +### Including Relationships + +To include relationships when **listing**, **showing**, or **creating** your resource, specify them in the `$include` and `$optionalInclude` properties on your controller: + +```php + // The relationships that are included by default. + public $include = ['user']; + + // Other relationships that are available to be included. + public $optionalInclude = ['discussions']; +``` + +You can then get a list of included relationships using the `extractInclude` method. This can be used to eager-load the relationships on your models before they are serialized: + +```php +$relations = $this->extractInclude($request); + +return Tag::all()->load($relations); +``` + +### Extending API Controllers + +It is possible to customize all of these options on _existing_ API controllers too via the `ApiController` extender + +```php +use Flarum\Api\Event\WillGetData; +use Flarum\Api\Controller\ListDiscussionsController; +use Illuminate\Contracts\Events\Dispatcher; + +return [ + (new Extend\ApiController(ListDiscussionsController::class)) + ->setSerializer(MyDiscussionSerializer::class) + ->addInclude('user') + ->addOptionalInclude('posts') + ->setLimit(20) + ->setMaxLimit(50) + ->setSort(['name' => 'asc']) + ->addSortField('firstName') + ->prepareDataQuery(function ($controller) { + // Add custom logic here to modify the controller + // before data queries are executed. + }) +] +``` + +The `ApiController` extender can also be used to adjust data before serialization + +```php +use Flarum\Api\Event\WillSerializeData; +use Flarum\Api\Controller\ListDiscussionsController; +use Illuminate\Contracts\Events\Dispatcher; + +return [ + (new Extend\ApiController(ListDiscussionsController::class)) + ->prepareDataForSerialization(function ($controller, $data, $request, $document) { + $data->load('myCustomRelation'); + }), +] +``` + +## Serializers + +Before we can send our data to the frontend, we need to convert it to JSON:API format so that it can be consumed by the frontend. +You should become familiar with the [JSON:API specification](https://jsonapi.org/format/). +Flarum's JSON:API layer is powered by the [tobscure/json-api](https://github.com/tobscure/json-api) library. + +A serializer is just a class that converts some data (usually [Eloquent models](models.md#backend-models)) into JSON:API. +Serializers serve as intermediaries between backend and frontend models: see the [model documentation](models.md) for more information. +To define a new resource type, create a new serializer class extending `Flarum\Api\Serializer\AbstractSerializer`. You must specify a resource `$type` and implement the `getDefaultAttributes` method which accepts the model instance as its only argument: + +```php +use Flarum\Api\Serializer\AbstractSerializer; +use Flarum\Api\Serializer\UserSerializer; + +class DiscussionSerializer extends AbstractSerializer +{ + protected $type = 'discussions'; + + protected function getDefaultAttributes($discussion) + { + return [ + 'title' => $discussion->title, + ]; + } +} +``` + +### Attributes and Relationships + +You can also specify relationships for your resource. Simply create a new method with the same name as the relation on your model, and return a call to `hasOne` or `hasMany` depending on the nature of the relationship. You must pass in the model instance and the name of the serializer to use for the related resources. + +```php + protected function user($discussion) + { + return $this->hasOne($discussion, UserSerializer::class); + } +``` + +### Extending Serializers + +To add **attributes** and **relationships** to an existing resource type, use the `ApiSerializer` extender: + +```php +use Flarum\Api\Serializer\UserSerializer; + +return [ + (new Extend\ApiSerializer(UserSerializer::class)) + // One attribute at a time + ->attribute('firstName', function ($serializer, $user, $attributes) { + return $user->first_name + }) + // Multiple modifications at once, more complex logic + ->mutate(function($serializer, $user, $attributes) { + $attributes['someAttribute'] = $user->someAttribute; + if ($serializer->getActor()->can('administrate')) { + $attributes['someDate'] = $serializer->formatDate($user->some_date); + } + + return $attributes; + }) + // API relationships + ->hasOne('phone', PhoneSerializer::class) + ->hasMany('comments', CommentSerializer::class), +] +``` + +### Non-Model Serializers and `ForumSerializer` + +Serializers don't have to correspond to Eloquent models: you can define JSON:API resources for anything. +For instance, Flarum core uses the [`Flarum\Api\Serializer\ForumSerializer`](https://api.docs.flarum.org/php/master/flarum/api/serializer/forumserializer) to send an initial payload to the frontend. This can include settings, whether the current user can perform certain actions, and other data. Many extensions add data to the payload by extending the attributes of `ForumSerializer`. \ No newline at end of file diff --git a/docs/tr/extend/assets.md b/docs/tr/extend/assets.md new file mode 100644 index 000000000..2fd7dedd2 --- /dev/null +++ b/docs/tr/extend/assets.md @@ -0,0 +1,8 @@ +# Extension Assets + +Some extensions might want to include assets like images or JSON files in their source code (note that this is not the same as uploads, which would probably require a [filesystem disk](filesystem.md)). + +This is actually very easy to do. Just create an `assets` folder at the root of your extension, and place any asset files there. +Flarum will then automatically copy those files to its own `assets` directory (or other storage location if [one is offered by extensions](filesystem.md)) every time the extension is enabled or [`php flarum assets:publish`](../console.md) is executed. + +If using the default storage driver, assets will be available at `https://FORUM_URL/assets/extensions/EXTENSION_ID/file.path`. However, since other extensions might use remote filesystems, we recommend serializing the url to assets you need in the backend. See [Flarum's serialization of the logo and favicon URLs](https://github.com/flarum/core/blob/bba6485effc088e38e9ae0bc8f25528ecbee3a7b/src/Api/Serializer/ForumSerializer.php#L85-L86) for an example. \ No newline at end of file diff --git a/docs/tr/extend/authorization.md b/docs/tr/extend/authorization.md index 16517da23..83d7dbe07 100644 --- a/docs/tr/extend/authorization.md +++ b/docs/tr/extend/authorization.md @@ -1,3 +1,206 @@ -# Yetki +# Authorization -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +As with any framework, Flarum allows certain actions and content to be restricted to certain users. There are 2 parallel systems for this: + +- The authorization process dictates whether a user can take a certain action. +- Visibility scoping can be applied to a database query to efficiently restrict the records that users can access. This is documented in our [model visibility](model-visibility.md) article. + +## Authorization Process + +The authorization process is used to check whether a person is allowed to perform certain actions. For instance, we want to check if a user is authorized before they: + +- Access the admin dashboard +- Start a discussion +- Edit a post +- Update another user's profile + +Each of these is determined by unique criteria: in some cases a flag is sufficient; otherwise, we might need custom logic. + +## How It Works + +Authorization queries are made with 3 parameters, with logic contained in [`Flarum\User\Gate`](https://api.docs.flarum.org/php/master/flarum/user/access/gate): + +1. The actor: the user attempting to perform the action +2. The ability: a string representing the action the actor is attempting +3. The arguments: usually an instance of a database model which is the subject of the attempted ability, but could be anything. + +First, we run the entire request (all three parameters) through all [policies](#policies) registered by extensions and core. Policies are blocks of logic provided by core and extensions that determine whether the actor can perform the ability on the arguments. Policies can return one of the following: + +- `Flarum\User\Access\AbstractPolicy::ALLOW` (via `$this->allow()`) +- `Flarum\User\Access\AbstractPolicy::DENY` (via `$this->deny()`) +- `Flarum\User\Access\AbstractPolicy::FORCE_ALLOW` (via `$this->forceAllow()`) +- `Flarum\User\Access\AbstractPolicy::FORCE_DENY` (via `$this->forceDeny()`) + +Policy results are considered in the priority `FORCE_DENY` > `FORCE_ALLOW` > `DENY` > `ALLOW`. For example, if a single policy returns `FORCE_DENY`, all other policies will be ignored. If one policy returns `DENY` and 10 policies return `ALLOW`, the request will be denied. This allows decisions to be made regardless of the order in which extensions are booted. Note that policies are extremely powerful: if access is denied at the policy stage, that will override group permissions and even admin privileges. + +Secondly, if all policies return null (or don't return anything), we check if the user is in a group that has a permission equal to the ability (note that both permissions and abilities are represented as strings). If so, we authorize the action. +See our [Groups and Permissions documentation](permissions.md) for more information on permissions. + +Then, if the user is in the admin group, we will authorize the action. + +Finally, as we have exhausted all checks, we will assume that the user is unauthorized and deny the request. + +## How To Use Authorization + +Flarum's authorization system is accessible through public methods of the `Flarum\User\User` class. The most important ones are listed below; others are documented in our [PHP API documentation](https://api.docs.flarum.org/php/master/flarum/user/user). + + +In this example, we will use `$actor` as an instance of `Flarum\User\User`, `'viewDiscussions'` and `'reply'` as examples of abilities, and `$discussion` (instance of `Flarum\Discussion\Discussion`) as an example argument. + +```php +// Check whether a user can perform an action. +$canDoSomething = $actor->can('viewDiscussions'); + +// Check whether a user can perform an action on a subject. +$canDoSomething = $actor->can('reply', $discussion); + +// Raise a PermissionDeniedException if a user cannot perform an action. +$actor->assertCan('viewDiscussions'); +$actor->assertCan('reply', $discussion); + +// Raise a NotAuthenticatedException if the user is not logged in. +$actor->assertRegistered(); + +// Raise a PermissionDeniedException if the user is not an admin. +$actpr->assertAdmin(); + +// Check whether one of the user's groups have a permission. +// WARNING: this should be used with caution, as it doesn't actually +// run through the authorization process, so it doesn't account for policies. +// It is, however, useful in implementing custom policies. +$actorHasPermission = $actor->hasPermission(`viewDiscussions`); +``` + +## Custom Policies + +Policies allow us to use custom logic beyond simple groups and permissions when evaluating authorization for an ability with a subject. For instance: + +- We want to allow users to edit posts even if they aren't moderators, but only their own posts. +- Depending on settings, we might allow users to rename their own discussions indefinitely, for a short period of time after posting, or not at all. + +As described [above](#how-it-works), on any authorization check, we query all policies registered for the target's model, or any parent classes of the target's model. +If no target is provided, any policies registered as `global` will be applied. + +So, how does a policy get "checked"? + +First, we check if the policy class has a method with the same name as the ability being evaluated. +If so, we run it with the actor and subject as parameters. +If that method returns a non-null value, we return that result. Otherwise, we continue to the next step (not necessarily the next policy). + +Then, we check if the policy class has a method called `can`. If so, we run it with the actor, ability, and subject, and return the result. + +If `can` doesn't exist or returns null, we are done with this policy, and we proceed to the next one. + +### Example Policies + +Let's take a look at an example policy from [Flarum Tags](https://github.com/flarum/tags/blob/master/src/Access): + +```php +is_restricted) { + return $actor->hasPermission('tag'.$tag->id.'.startDiscussion') ? $this->allow() : $this->deny(); + } + } + + /** + * @param User $actor + * @param Tag $tag + * @return bool|null + */ + public function addToDiscussion(User $actor, Tag $tag) + { + return $this->startDiscussion($actor, $tag); + } +} +``` + +We can also have global policies, which are run when `$user->can()` is called without a target model instance. Again from Tags: + +```php +settings = $settings; + } + + /** + * @param Flarum\User\User $actor + * @param string $ability + * @return bool|void + */ + public function can(User $actor, string $ability) + { + if (in_array($ability, ['viewDiscussions', 'startDiscussion'])) { + $enoughPrimary = count(Tag::getIdsWhereCan($actor, $ability, true, false)) >= $this->settings->get('min_primary_tags'); + $enoughSecondary = count(Tag::getIdsWhereCan($actor, $ability, false, true)) >= $this->settings->get('min_secondary_tags'); + + if ($enoughPrimary && $enoughSecondary) { + return $this->allow(); + } else { + return $this->deny(); + } + } + } +} +``` + +### Registering Policies + +Both model-based and global policies can be registered with the `Policy` extender in your `extend.php` file: + +```php +use Flarum\Extend; +use Flarum\Tags\Tag; +use YourNamespace\Access; + +return [ + // Other extenders + (new Extend\Policy()) + ->modelPolicy(Tag::class, Access\TagPolicy::class) + ->globalPolicy(Access\GlobalPolicy::class), + // Other extenders +]; +``` + +## Frontend Authorization + +Commonly, you'll want to use authorization results in frontend logic. +For example, if a user doesn't have permission to see search users, we shouldn't send requests to that endpoint. +And if a user doesn't have permission to edit users, we shouldn't show menu items for that. + +Because we can't do authorization checks in the frontend, we have to perform them in the backend, and attach them to serialization of data we're sending. +Global permissions (`viewDiscussions`, `viewUserList`) can be included on the `ForumSerializer`, but for object-specific authorization, we may want to include those with the subject object. +For instance, when we return lists of discussions, we check whether the user can reply, rename, edit, and delete them, and store that data on the frontend discussion model. +It's then accessible via `discussion.canReply()` or `discussion.canEdit()`, but there's nothing magic there: it's just another attribute sent by the serializer. + +For an example of how to attach data to a serializer, see a [similar case for transmitting settings](settings.md#accessing-settings). diff --git a/docs/tr/extend/backend-events.md b/docs/tr/extend/backend-events.md index b18b0d14d..fe8822679 100644 --- a/docs/tr/extend/backend-events.md +++ b/docs/tr/extend/backend-events.md @@ -1,3 +1,170 @@ -# Arka Uç Etkinlikleri +# Backend Events -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Often, an extension will want to react to some events occuring elsewhere in Flarum. For instance, we might want to increment a counter when a new discussion is posted, send a welcome email when a user logs in for the first time, or add tags to a discussion before saving it to the database. These events are known as **domain events**, and are broadcasted across the framework through [Laravel's event system](https://laravel.com/docs/8.x/events). + +:::warning Old Event API + +Historically, Flarum has used events for its extension API, emitting events like `GetDisplayName` or `ConfigureApiRoutes` to allow extensions to insert logic into various parts of Flarum. These events are gradually being phased out in favor of the declarative [extender system](start.md#extenders), and will be removed before stable. Domain events will not be removed. + +::: + +For a full list of backend events, see our [API documentation](https://api.docs.flarum.org/php/master/search.html?search=Event). Domain events classes are organized by namespace, usually `Flarum\TYPE\Event`. + +## Listening to Events + +You can attach a listener to an event using the [`Event`](https://api.docs.flarum.org/php/master/flarum/extend/event) [extender](start.md#extenders): + +```php +use Flarum\Extend; +use Flarum\Post\Event\Deleted; +use Symfony\Contracts\Translation\TranslatorInterface; + + +return [ + (new Extend\Event) + ->listen(Deleted::class, function($event) { + // do something here + }) + ->listen(Deleted::class, PostDeletedListener::class) +]; + + +class PostDeletedListener +{ + protected $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + + public function handle(Deleted $event) + { + // Your logic here + } +} +``` + +As shown above, a listener class can be used instead of a callback. This allows you to [inject dependencies](https://laravel.com/docs/8.x/container) into your listener class via constructor parameters. In this example we resolve a translator instance, but we can inject anything we want/need. + +You can also listen to multiple events at once via an event subscriber. This is useful for grouping common functionality; for instance, if you want to update some metadata on changes to posts: + +```php +use Flarum\Extend; +use Flarum\Post\Event\Deleted; +use Flarum\Post\Event\Saving; +use Symfony\Contracts\Translation\TranslatorInterface; + + +return [ + (new Extend\Event) + ->subscribe(PostEventSubscriber::class), +]; + + +class PostEventSubscriber +{ + protected $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + + public function subscribe($events) + { + $events->listen(Deleted::class, [$this, 'handleDeleted']); + $events->listen(Saving::class, [$this, 'handleSaving']); + } + + public function handleDeleted(Deleted $event) + { + // Your logic here + } + + public function handleSaving(Saving $event) + { + // Your logic here + } +} +``` + +## Dispatching Events + +Dispatching events is very simple. All you need to do is inject `Illuminate\Contracts\Events\Dispatcher` into your class, and then call its `dispatch` method. For instance: + +```php +use Flarum\Post\Event\Deleted; +use Illuminate\Contracts\Events\Dispatcher; + + +class SomeClass +{ + /** + * @var Dispatcher + */ + protected $events; + + /** + * @param Dispatcher $events + */ + public function __construct(Dispatcher $events) + { + $this->events = $events; + } + + public function someMethod() + { + // Logic + $this->events->dispatch( + new Deleted($somePost, $someActor) + ); + // More Logic + } +} +``` + +## Custom Events + +As an extension developer you can define your own events to allow yourself (or other extensions) to react to events in your extension. +Events are generally instances of simple classes (no need to extend anything). When defining a new event, you'll typically want to use public properties, and maybe some methods for convenience of users. +For example, if we take a look at `Flarum\Post\Event\Deleted`, it's just a wrapping around some data: + +```php +post = $post; + $this->actor = $actor; + } +} +``` diff --git a/docs/tr/extend/console.md b/docs/tr/extend/console.md index 3df610e03..158f05fd0 100644 --- a/docs/tr/extend/console.md +++ b/docs/tr/extend/console.md @@ -1,3 +1,61 @@ -# Konsol +# Console -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Flarum allows extension developers to add custom console commands in addition to the [default ones](../console.md) provided by flarum core. + +All console command development is done in the backend using PHP. To create a custom console command, you'll need to create a class that extends `\Flarum\Console\AbstractCommand`. + +```php +use Flarum\Console\AbstractCommand; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; + +class YourCommand extends AbstractCommand { + protected function configure() + { + $this + ->setName('YOUR COMMAND NAME') + ->setDescription('YOUR COMMAND DESCRIPTION'); + } + protected function fire() + { + // Your logic here! + } +} +``` + +## Registering Console Commands + +To register console commands, use the `Flarum\Extend\Console` extender in your extension's `extend.php` file: + +```php +use Flarum\Extend; +use YourNamespace\Console\CustomCommand; + +return [ + // Other extenders + (new Extend\Console())->command(CustomCommand::class) + // Other extenders +]; +``` + +## Scheduled Commands + +The `Flarum\Extend\Console`'s `schedule` method allows extension developers to create scheduled commands that run on an interval: + + +```php +use Flarum\Extend; +use YourNamespace\Console\CustomCommand; +use Illuminate\Console\Scheduling\Event; + +return [ + // Other extenders + (new Extend\Console())->schedule('cache:clear', function (Event $event) { + $event->everyMinute(); + }, ['Arg1', '--option1', '--option2']), + // Other extenders +]; +``` + +In the callback provided as the second argument, you can call methods on the [$event object](https://laravel.com/api/8.x/Illuminate/Console/Scheduling/Event.html) to schedule on a variety of frequencies (or apply other options, such as only running on one server). See the [Laravel documentation](https://laravel.com/docs/8.x/scheduling#scheduling-artisan-commands) for more information. \ No newline at end of file diff --git a/docs/tr/extend/distribution.md b/docs/tr/extend/distribution.md index 40d7cfe70..9f5f82566 100644 --- a/docs/tr/extend/distribution.md +++ b/docs/tr/extend/distribution.md @@ -1,3 +1,41 @@ -# Dağıtım +# Distribution -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +You've written a great extension — and now you want the whole world to be able to use it. This document will take you through the process of distribution, from setting up a Git repository for your extension, to publishing it on Packagist. + +## Setting Up Git + +The first thing you'll need to do is set up a version control system (VCS). +The most popular VCS is [Git](https://git-scm.com/). In this guide we'll be using Git, so make sure you have it [installed](https://git-scm.com/downloads) before continuing. If you don't have much Git knowledge, you may want to check out [these learning resources](https://try.github.io/). + +After you have installed Git, you'll need to initialize your repository. You can use `git init` on the command line if you're comfortable, or use a GUI tool like [SourceTree](https://www.sourcetreeapp.com/) or [GitKraken](https://www.gitkraken.com/). + +Then, you'll need an account in a Git hosting server, the most popular being [GitHub](https://github.com) and [GitLab](https://gitlab.com). These will instruct you on how to hook up your local repository with the online "remote" repository. + +## Tagging a Release + +As you are going to be publishing this extension, you'll want to make sure that the information is up to date. Take a minute to revisit `composer.json` and make sure package name, description, and Flarum extension information are all correct. It is recommended to have a `README.md` file in your repository to explain what the extension is, so create one if you haven't already. + +When you're ready to release, commit your extension's files to the repo and tag your first version: + +```bash +git tag v0.1.0 +git push && git push --tags +``` + +## Publishing on Packagist + +Composer packages are published to a Composer repository, usually [Packagist](https://packagist.org/). You will need an account to proceed. + +If this is the first release you are publishing of your extension, you will need to [submit your package](https://packagist.org/packages/submit) using its public repository URL. If your extension is located on GitHub, this URL will look something like `https://github.com/AUTHOR/NAME.git`. + +### Future Releases + +You can set up Packagist to [auto-update packages](https://packagist.org/about#how-to-update-packages). Then for future releases, all you will need to do with Git is commit, tag, and push it to the remote server. + +## Promoting Your Extension + +You will most likely want to create a discussion on the Flarum Community in the [Extensions tag](https://discuss.flarum.org/t/extensions). Other people can install your extension using the following command: + +```bash +composer require vendor/package +``` \ No newline at end of file diff --git a/docs/tr/extend/extending-extensions.md b/docs/tr/extend/extending-extensions.md new file mode 100644 index 000000000..0fc9156ca --- /dev/null +++ b/docs/tr/extend/extending-extensions.md @@ -0,0 +1,125 @@ +# Extending Extensions + +Flarum extensions aren't just for adding features to core: extensions can extend other extensions! + +::: tip + +To learn how to make your extension extensible, see the [relevant documentation](extensibility.md) + +::: + +## Dependencies + +If your extension relies on another extension, you'll want to ensure that: + +- The other extension is installed and enabled before yours can be. +- The other extension can't be disabled while yours is enabled. +- The other extension is booted before your extension. + +Flarum makes this very easy: just add the other extension to your extension's `composer.json`'s `require` section. + +For example, if you were building a new theme for the Flarum Tags extension, your `composer.json` would look like this: + +```json +{ + // ... + "require": { + "flarum/core": "^0.1.0-beta.15", // Since all extensions need to require core. + "flarum/tags": "^0.1.0-beta.15" // This tells Flarum to treat tags as a dependency of your extension. + }, + // ... +} +``` + +## Optional Dependencies + +Sometimes, extension A might want to extend extension B only if extension B is enabled. +In this case, we call B an "Optional Dependency" of A. +For instance, a drafts extension might want to add support for saving private discussion drafts, but only if the private discussion extension is enabled. + +The first step here is detecting whether extension B is enabled. In the frontend, this is easy: if extension B does anything in the frontend, its extension ID will appear as a key in the `flarum.extensions` global object. For instance: + +```js +if ('some-extension-id' in flarum.extensions) { + // do something +} +``` + +In the backend, you'll need to inject an instance of `Flarum\Extension\ExtensionManager`, and use its `isEnabled()` method. For instance: + +```php +extensions = $extensions; + } + + public function someMethod() + { + if ($this->extensions->isEnabled('some-extension-id')) { + // do something. + } + } +} +``` + +Generally, if your extension has optional dependencies, you'll want it to be booted after said optional dependencies. +You can also do this by specifying composer package names (NOT flarum extension IDs) in an array for the `extra.flarum-extension.optional-dependencies` key of your composer.json. + +For instance: + +```json +{ + // ... + "extra": { + "flarum-extension": { + "optional-dependencies": [ + "flarum/tags" + ] + } + }, + // ... +} +``` + +## Importing from Extensions + +In the backend, you can import the classes you need via regular PHP `use` statements: + +```php + { + // Your Extension Code Here +}) + +export { + // Put all the stuff you want to export here. +} +``` diff --git a/docs/tr/extend/filesystem.md b/docs/tr/extend/filesystem.md new file mode 100644 index 000000000..14159aa8c --- /dev/null +++ b/docs/tr/extend/filesystem.md @@ -0,0 +1,132 @@ +# Filesystem + +Flarum core integrates with the filesystem to store and serve assets (like compiled JS/CSS or upload logos/favicons) and avatars. + +Extensions can use Flarum's provided utils for their own filesystem interaction and file storage needs. This system is based around [Laravel's filesystem tools](https://laravel.com/docs/8.x/filesystem), which are in turn based on the [Flysystem library](https://github.com/thephpleague/flysystem). + +## Disks + +Filesystem **disks** represent storage locations, and are backed by storage drivers, which we'll cover later. +Flarum core has 2 disks: `flarum-assets` and `flarum-avatars`. + +### Using existing disks + +To access a disk, you'll need to retrieve it from the [Filesystem Factory](https://laravel.com/api/8.x/Illuminate/Contracts/Filesystem/Factory.html). +To do so, you should inject the factory contract in your class, and access the disks you need. + +Let's take a look at core's [`DeleteLogoController`](https://github.com/flarum/core/blob/bba6485effc088e38e9ae0bc8f25528ecbee3a7b/src/Api/Controller/DeleteLogoController.php#L19-L59) for an example: + +```php +settings = $settings; + $this->uploadDir = $filesystemFactory->disk('flarum-assets'); + } + + /** + * {@inheritdoc} + */ + protected function delete(ServerRequestInterface $request) + { + RequestUtil::getActor($request)->assertAdmin(); + + $path = $this->settings->get('logo_path'); + + $this->settings->set('logo_path', null); + + if ($this->uploadDir->exists($path)) { + $this->uploadDir->delete($path); + } + + return new EmptyResponse(204); + } +} +``` + +The object returned by `$filesystemFactory->disk(DISK_NAME)` implements the [Illuminate\Contracts\Filesystem\Cloud](https://laravel.com/api/8.x/Illuminate/Contracts/Filesystem/Cloud.html) interface, and can be used to create/get/move/delete files, and to get the URL to a resource. + +### Declaring new disks + +Some extensions will want to group their resources / uploads onto a custom disk as opposed to using `flarum-assets` or `flarum-avatars`. + +This can be done via the `Filesystem` extender: + +```php +use Flarum\Extend; + +return [ + (new Extend\Filesystem) + ->disk('flarum-uploads', function (Paths $paths, UrlGenerator $url) { + return [ + 'root' => "$paths->public/assets/uploads", + 'url' => $url->to('forum')->path('assets/uploads') + ]; + }); +``` + +Since all disks use the local filesystem by default, you'll need to provide a base path and base URL for the local filesystem. + +The config array can contain other entries supported by [Laravel disk config arrays](https://laravel.com/docs/8.x/filesystem#configuration). The `driver` key should not be provided, and will be ignored. + +## Storage drivers + +Flarum selects the active driver for each disk by checking the `disk_driver.DISK_NAME` key in the [settings repository](settings.md) and [config.php file](../config.md). If no driver is configured, or the configured driver is unavailable, Flarum will default to the `local` driver. + +You can define new storage drivers by implementing the [`Flarum\Filesystem\DriverInterface` interface](https://github.com/flarum/core/blob/bba6485effc088e38e9ae0bc8f25528ecbee3a7b/src/Filesystem/DriverInterface.php#L16-L16), and registering it via the `Filesystem` extender: + +```php +use Flarum\Extend; + +return [ + (new Extend\Filesystem) + ->driver('aws-with-cdn', AwsWithCdnDriver::class); +``` + +Filesystem storage drivers are a very powerful tool that allows you to completely customize file storage locations, attach arbitrary CDNs, and otherwise extend the filesystem / cloud storage integration layer. + +::: danger + +Some drivers might try to index their filesystem every time the driver is instantiated, even if only the `url` method is needed. This can have serious performance implications. In most cases, you'll want to ensure that your driver's `url` method does not ping the remote filesystem. Similarly, the remote filesystem should usually not be accessed until operations are actually executed. + +::: + +## GUI and Admin Configuration + +Flarum does not currently provide a GUI for selecting drivers for disks, or for entering settings for drivers. This might be added in the future. +For now, extensions are responsible for providing a GUI for their disks and drivers. + +As noted [above](#storage-drivers), if your extension provides a GUI for selecting drivers for a disk, it should modify the `disk_driver.DISK_NAME` key in settings. diff --git a/docs/tr/extend/formatting.md b/docs/tr/extend/formatting.md index 1b0ec2b62..d4ea23b12 100644 --- a/docs/tr/extend/formatting.md +++ b/docs/tr/extend/formatting.md @@ -1,3 +1,42 @@ -# Biçimlendirme +# Formatting -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Flarum uses the powerful [s9e TextFormatter](https://github.com/s9e/TextFormatter) library to format posts from plain markup into HTML. You should become familiar with [how TextFormatter works](https://s9etextformatter.readthedocs.io/Getting_started/How_it_works/) before you attempt to extend it. + +In Flarum, post content is formatted with a minimal TextFormatter configuration by default. The bundled **Markdown** and **BBCode** extensions simply enable the respective plugins on this TextFormatter configuration. + +## Configuration + +You can configure the TextFormatter `Configurator` instance, as well as run custom logic during parsing and rendering, using the `Formatter` extender: + +```php +use Flarum\Extend; +use Psr\Http\Message\ServerRequestInterface as Request; +use s9e\TextFormatter\Configurator; +use s9e\TextFormatter\Parser; +use s9e\TextFormatter\Renderer; + +return [ + (new Extend\Formatter) + // Add custom text formatter configuration + ->configure(function (Configurator $config) { + $config->BBCodes->addFromRepository('B'); + }) + // Modify raw text before it is parsed. + // This callback should return the modified text. + ->parse(function (Parser $parser, $context, $text) { + // custom logic here + return $newText; + }) + // Modify the XML to be rendered before rendering. + // This callback should return the new XML. + // For example, in the mentions extension, this is used to + // provide the username and display name of the user being mentioned. + // Make sure that the last $request argument is nullable (or omitted entirely). + ->render(function (Renderer $renderer, $context, $xml, Request $request = null) { + // custom logic here + return $newXml; + }) +]; +``` + +With a good understanding of TextFormatter, this will allow you to achieve anything from simple BBCode tag additions to more complex formatting tasks like Flarum's **Mentions** extension. diff --git a/docs/tr/extend/forms.md b/docs/tr/extend/forms.md index 39b7d5bf1..803793b17 100644 --- a/docs/tr/extend/forms.md +++ b/docs/tr/extend/forms.md @@ -1,3 +1,114 @@ -# Formlar ve İstekler +# Forms and Requests -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +In this article, we'll go over some frontend tools that are available to us for building and managing forms, as well how to send HTTP requests via Flarum. + +## Form Components + +As with any interactive site, you will likely want to include forms in some pages and modals. +Flarum provides some components to make building (and styling!) these forms easier. +Please see the linked API documentation for each of these to learn more about its accepted attrs. + +- The [`flarum/common/components/FieldSet` component](https://api.docs.flarum.org/js/master/class/src/common/components/fieldset.js~fieldset) wraps its children in a HTML fieldset tag, with a legend. +- The [`flarum/common/components/Select` component](https://api.docs.flarum.org/js/master/class/src/common/components/select.js~select) is a stylized select input. +- The [`flarum/common/components/Switch`](https://api.docs.flarum.org/js/master/class/src/common/components/switch.js~switch) and [`flarum/common/components/Checkbox` components](https://api.docs.flarum.org/js/master/class/src/common/components/checkbox.js~checkbox) are stylized checkbox input components. Their `loading` attr can be set to `true` to show a loading indicator. +- The [`flarum/common/components/Button` component](https://api.docs.flarum.org/js/master/class/src/common/components/button.js~button) is a stylized button, and is used frequently throughout Flarum. + +You'll typically want to assign logic for reacting to input changes via Mithril's `on*` attrs, not external listeners (as is common with jQuery or plain JS). For example: + +```jsx +import Component from 'flarum/common/Component'; +import FieldSet from 'flarum/common/components/FieldSet'; +import Button from 'flarum/common/components/Button'; +import Switch from 'flarum/common/components/Switch'; + + +class FormComponent extends Component { + oninit(vnode) { + this.textInput = ""; + this.booleanInput = false; + } + + view() { + return ( +
+
+ this.textInput = e.target.value}> + + this.booleanInput = val}> + +
+ +
+ ) + } + + onsubmit() { + // Some form handling logic here + } +} +``` + +Don't forget to use [translations](translate.md)! + + +## Streams, bidi, and withAttr + +Flarum provides [Mithril's Stream](https://mithril.js.org/stream.html) as `flarum/common/util/Stream`. +This is a very powerful reactive data structure, but is most commonly used in Flarum as a wrapper for form data. +Its basic usage is: + +```js +import Stream from 'flarum/common/utils/Stream'; + + +const value = Stream("hello!"); +value() === "hello!"; // true +value("world!"); +value() === "world!"; // true +``` + +In Flarum forms, streams are frequently used together with the bidi attr. +Bidi stands for bidirectional binding, and is a common pattern in frontend frameworks. Flarum patches Mithril with the [`m.attrs.bidi` library](https://github.com/tobyzerner/m.attrs. +This abstracts away input processing in Mithril. For instance: + +```jsx +import Stream from 'flarum/common/utils/Stream'; + +const value = Stream(); + +// Without bidi + value(e.target.value)}> + +// With bidi + +``` + +You can also use the `flarum/common/utils/withAttr` util for simplified form processing. `withAttr` calls a callable, providing as an argument some attr of the DOM element tied to the component in question: + +```jsx +import Stream from 'flarum/common/utils/Stream'; +import withAttr from 'flarum/common/utils/withAttr'; + +const value = Stream(); + +// With a stream + + +// With any callable + { + // Some custom logic here +})}> +``` + +## Making Requests + +In our [models](models.md) documentation, you learned how to work with models, and save model creation, changes, and deletion to the database via the Store util, which is just a wrapper around Flarum's request system, which itself is just a wrapper around [Mithril's request system](https://mithril.js.org/request.html). + +Flarum's request system is available globally via `app.request(options)`, and has the following differences from Mithril's `m.request(options)`: + +- It will automatically attach `X-CSRF-Token` headers. +- It will convert `PATCH` and `DELETE` requests into `POST` requests, and attach a `X-HTTP-Method-Override` header. +- If the request errors, it will show an alert which, if in debug mode, can be clicked to show a full error modal. +- You can supply a `background: false` option, which will run the request synchronously. However, this should almost never be done. + +Otherwise, the API for using `app.request` is the same as that for `m.request`. diff --git a/docs/tr/extend/frontend-pages.md b/docs/tr/extend/frontend-pages.md index c659b016d..33ac5f472 100644 --- a/docs/tr/extend/frontend-pages.md +++ b/docs/tr/extend/frontend-pages.md @@ -1,3 +1,225 @@ -# Ön Uç Sayfaları ve Çözücüler +# Frontend Pages and Resolvers -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +As explained in the [Routes and Content](routes.md#frontend-routes) documentation, we can use Mithril's routing system to show different [components](frontend.md#components) for different routes. Mithril allows you to use any component you like, even a Modal or Alert, but we recommend sticking to component classes that inherit the `Page` component. + +## The Page Component + +We provide `flarum/common/components/Page` as a base class for pages in both the `admin` and `forum` frontends. It has a few benefits: + +- Automatically updates [`app.current` and `app.previous` PageState](#pagestate) when switching from one route to another. +- Automatically closes the modal and drawer when switching from one route to another. +- Applies `this.bodyClass` (if defined) to the '#app' HTML element when the page renders. +- It's also good for consistency's sake to use a common base class for all pages. +- If the page's `scrollTopOnCreate` attribute is set to `false` in `oninit`, the page won't be scrolled to the top when changed. +- If the page's `useBrowserScrollRestoration` is set to `false` in `oninit`, the browser's automatic scroll restoration won't be used on that page. + +Page components work just like any other inherited component. For a (very simple) example: + +```js +import Page from 'flarum/common/components/Page'; + + +export default class CustomPage extends Page { + view() { + return

Hello!

+ } +} +``` + +### Setting Page as Homepage + +Flarum uses a setting to determine which page should be the homepage: this gives admins flexibility to customize their communities. +To add your custom page to the homepage options in Admin, you'll need to extend the `BasicsPage.homePageItems` method with your page's path. + +An example from the [Tags extension](https://github.com/flarum/tags/blob/master/js/src/admin/addTagsHomePageOption.js): + +```js +import { extend } from 'flarum/common/extend'; +import BasicsPage from 'flarum/common/components/BasicsPage'; + +export default function() { + extend(BasicsPage.prototype, 'homePageItems', items => { + items.add('tags', { + path: '/tags', + label: app.translator.trans('flarum-tags.admin.basics.tags_label') + }); + }); +} +``` + +To learn how to set up a path/route for your custom page, see the [relevant documentation](routes.md). + +### Page Titles + +Often, you'll want some custom text to appear in the browser tab's title for your page. +For instance, a tags page might want to show "Tags - FORUM NAME", or a discussion page might want to show the title of the discussion. + +To do this, your page should include calls to `app.setTitle()` and `app.setTitleCount()` in its `oncreate` [lifecycle hook](frontend.md) (or when data is loaded, if it pulls in data from the API). + +For example: + +```js +import Page from 'flarum/common/components/Page'; + + +export default class CustomPage extends Page { + oncreate(vnode) { + super.oncreate(vnode); + + app.setTitle("Cool Page"); + app.setTitleCount(0); + } + + view() { + // ... + } +} + +export default class CustomPageLoadsData extends Page { + oninit(vnode) { + super.oninit(vnode); + + app.store.find("users", 1).then(user => { + app.setTitle(user.displayName()); + app.setTitleCount(0); + }) + } + + view() { + // ... + } +} +``` + +Please note that if your page is [set as the homepage](#setting-page-as-homepage), `app.setTitle()` will clear the title for simplicity. +It should still be called though, to prevent titles from previous pages from carrying over. + +## PageState + +Sometimes, we want to get information about the page we're currently on, or the page we've just come from. +To allow this, Flarum creates (and stores) instances of [`PageState`](https://api.docs.flarum.org/js/master/class/src/common/states/pagestate.js~pagestate) as `app.current` and `app.previous`. +These store: + +- The component class being used for the page +- A collection of data that each page sets about itself. The current route name is always included. + +Data can be set to, and retrieved from, Page State using: + +```js +app.current.set(KEY, DATA); +app.current.get(KEY); +``` + +For example, this is how the Discussion Page makes its [`PostStreamState`](https://api.docs.flarum.org/js/master/class/src/forum/states/poststreamstate.js~poststreamstate) instance globally available. + +You can also check the type and data of a page using `PostStreamState`'s `matches` method. For instance, if we want to know if we are currently on a discussion page: + +```jsx +import IndexPage from 'flarum/forum/components/DiscussionPage'; +import DiscussionPage from 'flarum/forum/components/DiscussionPage'; + +// To just check page type +app.current.matches(DiscussionPage); + +// To check page type and some data +app.current.matches(IndexPage, {routeName: 'following'}); +``` + +## Admin Pages + +See the [Admin Dashboard documentation](admin.md) for more information on tools specifically available to admin pages (and how to override the admin page for your extension). + +## Route Resolvers (Advanced) + +[Advanced use cases](https://mithril.js.org/route.html#advanced-component-resolution) can take advantage of Mithril's [route resolver system](https://mithril.js.org/route.html#routeresolver). +Flarum actually already wraps all its components in the `flarum/common/resolvers/DefaultResolver` resolver. This has the following benefits: + +- It passes a `routeName` attr to the current page, which then provides it to `PageState` +- It assigns a [key](https://mithril.js.org/keys.html#single-child-keyed-fragments) to the top level page component. When the route changes, if the top level component's key has changed, it will be completely rerendered (by default, Mithril does not rerender components when switching from one page to another if both are handled by the same component). + +### Using Route Resolvers + +There are actually 3 ways to set the component / route resolver when registering a route: + +- the `resolver` key can be used to provide an **instance** of a route resolver. This instance should define which component should be used, and hardcode the route name to be passed into it. This instance will be used without any modifications by Flarum. +- The `resolverClass` key AND `component` key can be used to provide a **class** that will be used to instantiate a route resolver, to be used instead of Flarum's default one, as well as the component to use. Its constructor should take 2 arguments: `(component, routeName)`. +- The `component` key can be used alone to provide a component. This will result in the default behavior. + +For example: + +```js +// See above for a custom page example +import CustomPage from './components/CustomPage'; +// See below for a custom resolver example +import CustomPageResolver from './resolvers/CustomPageResolver'; + +// Use a route resolver instance +app.routes['resolverInstance'] = {path: '/custom/path/1', resolver: { + onmatch: function(args) { + if (!app.session.user) return m.route.SKIP; + + return CustomPage; + } +}}; + +// Use a custom route resolver class +app.routes['resolverClass'] = {path: '/custom/path/2', resolverClass: CustomPageResolver, component: CustomPage}; + +// Use the default resolver class (`flarum/common/resolvers/DefaultResolver`) +app.routes['resolverClass'] = {path: '/custom/path/2', component: CustomPage}; +``` + +### Custom Resolvers + +We strongly encourage custom route resolvers to extend `flarum/common/resolvers/DefaultResolver`. +For example, Flarum's `flarum/forum/resolvers/DiscussionPageResolver` assigns the same key to all links to the same discussion (regardless of the current post), and triggers scrolling when using `m.route.set` to go from one post to another on the same discussion page: + +```js +import DefaultResolver from '../../common/resolvers/DefaultResolver'; + +/** + * This isn't exported as it is a temporary measure. + * A more robust system will be implemented alongside UTF-8 support in beta 15. + */ +function getDiscussionIdFromSlug(slug: string | undefined) { + if (!slug) return; + return slug.split('-')[0]; +} + +/** + * A custom route resolver for DiscussionPage that generates the same key to all posts + * on the same discussion. It triggers a scroll when going from one post to another + * in the same discussion. + */ +export default class DiscussionPageResolver extends DefaultResolver { + static scrollToPostNumber: number | null = null; + + makeKey() { + const params = { ...m.route.param() }; + if ('near' in params) { + delete params.near; + } + params.id = getDiscussionIdFromSlug(params.id); + return this.routeName.replace('.near', '') + JSON.stringify(params); + } + + onmatch(args, requestedPath, route) { + if (route.includes('/d/:id') && getDiscussionIdFromSlug(args.id) === getDiscussionIdFromSlug(m.route.param('id'))) { + DiscussionPageResolver.scrollToPostNumber = parseInt(args.near); + } + + return super.onmatch(args, requestedPath, route); + } + + render(vnode) { + if (DiscussionPageResolver.scrollToPostNumber !== null) { + const number = DiscussionPageResolver.scrollToPostNumber; + // Scroll after a timeout to avoid clashes with the render. + setTimeout(() => app.current.get('stream').goToNumber(number)); + DiscussionPageResolver.scrollToPostNumber = null; + } + + return super.render(vnode); + } +} +``` diff --git a/docs/tr/extend/frontend.md b/docs/tr/extend/frontend.md index ad64d8c6b..f7c472d82 100644 --- a/docs/tr/extend/frontend.md +++ b/docs/tr/extend/frontend.md @@ -1,3 +1,399 @@ -# Ön Uç Geliştirme +# Frontend Development -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +This page describes how to make changes to Flarum's user interface. How to add buttons, marquees, and blinking text. 🤩 + +[Remember](/extend/start.md#architecture), Flarum's frontend is a **single-page JavaScript application**. There's no Twig, Blade, or any other kind of PHP template to speak of. The few templates that are present in the backend are only used to render search-engine-optimized content. All changes to the UI need to be made via JavaScript. + +Flarum has two separate frontend applications: + +* `forum`, the public side of your forum where users create discussions and posts. +* `admin`, the private side of your forum where, as an administrator of your forum, you configure your Flarum installation. + +They share the same foundational code, so once you know how to extend one, you know how to extend both. + +:::tip Typings! + +Use our new [Typing Library](https://www.npmjs.com/package/flarum) as a dev dependency for editor autocomplete to make frontend development easier! + +::: + +## Transpilation and File Structure + +This portion of the guide will explain the necessary file setup for extensions. Once again, we highly recommend using the unofficial [FoF extension generator](https://github.com/FriendsOfFlarum/extension-generator) to set up the file structure for you. That being said, you should still read this to understand what's going on beneath the surface. + +Before we can write any JavaScript, we need to set up a **transpiler**. This allows us to use [TypeScript](https://www.typescriptlang.org/) and its magic in Flarum core and extensions. + +In order to do this transpilation, you need to be working in a capable environment. No, not the home/office kind of environment – you can work in the bathroom for all I care! I'm talking about the tools that are installed on your system. You'll need: + +* Node.js and npm ([Download](https://nodejs.org/en/download/)) +* Webpack (`npm install -g webpack`) + +This can be tricky because everyone's system is different. From the OS you're using, to the program versions you have installed, to the user access permissions – I get chills just thinking about it! If you run into trouble, ~~tell him I said hi~~ use [Google](https://google.com) to see if someone has encountered the same error as you and found a solution. If not, ask for help from the [Flarum Community](https://discuss.flarum.org) or on the [Discord chat](https://flarum.org/discord/). + +It's time to set up our little JavaScript transpilation project. Create a new folder in your extension called `js`, then pop in a couple of new files. A typical extension will have the following frontend structure: + +``` +js +├── dist (compiled js is placed here) +├── src +│ ├── admin +│ └── forum +├── admin.js +├── forum.js +├── package.json +└── webpack.config.json +``` + +### package.json + +```json +{ + "private": true, + "name": "@acme/flarum-hello-world", + "dependencies": { + "flarum-webpack-config": "0.1.0-beta.10", + "webpack": "^4.0.0", + "webpack-cli": "^3.0.7" + }, + "dev-dependencies": { + "flarum": "0.1.0-beta.16", + }, + "scripts": { + "dev": "webpack --mode development --watch", + "build": "webpack --mode production" + } +} +``` + +This is a standard JS [package-description file](https://docs.npmjs.com/files/package.json), used by npm and Yarn (Javascript package managers). You can use it to add commands, js dependencies, and package metadata. We're not actually publishing a npm package: this is simply used to collect dependencies. + +Please note that we do not need to include `flarum/core` or any flarum extensions as dependencies: they will be automatically packaged when Flarum compiles the frontends for all extensions. + +### webpack.config.js + +```js +const config = require('flarum-webpack-config'); + +module.exports = config(); +``` + +[Webpack](https://webpack.js.org/concepts/) is the system that actually compiles and bundles all the javascript (and its dependencies) for our extension. +To work properly, our extensions should use the [official flarum webpack config](https://github.com/flarum/flarum-webpack-config) (shown in the above example). + +### admin.js and forum.js + +These files contain the root of our actual frontend JS. You could put your entire extension here, but that would not be well organized. For this reason, we recommend putting the actual +source code in `src`, and having these files just export the contents of `src`. For instance: + +```js +// admin.js +export * from './src/admin'; + +// forum.js +export * from './src/forum'; +``` + +### src + +If following the recommendations for `admin.js` and `forum.js`, we'll want to have 2 subfolders here: one for `admin` frontend code, and one for `forum` frontend code. +If you have components, models, utils, or other code that is shared across both frontends, you may want to create a `common` subfolder and place it there. + +Structure for `admin` and `forum` is identical, so we'll just show it for `forum` here: + +``` +src/forum/ +├── components/ +|-- models/ +├── utils/ +└── index.js +``` + +`components`, `models`, and `utils` are directories that contain files where you can define custom [components](#components), [models](models.md#frontend-models), and reusable util helper functions. +Please note that this is all simply a recommendation: there's nothing forcing you to use this particular file structure (or any other file structure). + +The most important file here is `index.js`: everything else is just extracting classes and functions into their own files. Let's go over a typical `index.js` file structure: + +```js +import {extend, override} from 'flarum/common/extend'; + +// We provide our extension code in the form of an "initializer". +// This is a callback that will run after the core has booted. +app.initializers.add('our-extension', function(app) { + // Your Extension Code Here + console.log("EXTENSION NAME is working!"); +}); +``` + +We'll go over tools available for extensions below. + +### Importing + +You should familiarize yourself with proper syntax for [importing js modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import), as most extensions larger than a few lines will split their js into multiple files. + +Pretty much every Flarum extension will need to import *something* from Flarum Core. +Like most extensions, core's JS source code is split up into `admin`, `common`, and `forum` folders. You can import the file by prefixing its path in the Flarum core source code with `flarum`. So `admin/components/AdminLinkButton` is available as `flarum/admin/components/AdminLinkButton`, `common/Component` is available as `flarum/common/Component`, and `forum/states/PostStreamState` is available as `flarum/forum/states/PostStreamState`. + +In some cases, an extension may want to extend code from another flarum extension. This is only possible for extensions which explicitly export their contents. + +* `flarum/tags` and `flarum/flags` are currently the only bundled extensions that allow extending their JS. You can import their contents from `flarum/{EXT_NAME}/PATH` (e.g. `flarum/tags/components/TagHero`). +* The process for extending each community extension is different; you should consult documentation for each individual extension. + +### Transpilation + +OK, time to fire up the transpiler. Run the following commands in the `js` directory: + +```bash +npm install +npm run dev +``` + +This will compile your browser-ready JavaScript code into the `js/dist/forum.js` file, and keep watching for changes to the source files. Nifty! + +When you've finished developing your extension (or before a new release), you'll want to run `npm run build` instead of `npm run dev`: this builds the extension in production mode, which makes the source code smaller and faster. + +## Asset Registration + +### JavaScript + +In order for your extension's JavaScript to be loaded into the frontend, we need to tell Flarum where to find it. We can do this using the `Frontend` extender's `js` method. Add it to your extension's `extend.php` file: + +```php +js(__DIR__.'/js/dist/forum.js') +]; +``` + +Flarum will make anything you `export` from `forum.js` available in the global `flarum.extensions['acme-hello-world']` object. Thus, you may choose to expose your own public API for other extensions to interact with. + +::: tip External Libraries + +Only one main JavaScript file per extension is permitted. If you need to include any external JavaScript libraries, either install them with NPM and `import` them so they are compiled into your JavaScript file, or see [Routes and Content](/extend/routes.md) to learn how to add extra `'; + }) +]; +``` + +You can also add content onto your frontend route registrations: + +```php +return [ + (new Extend\Frontend('forum')) + ->route('/users', 'acme.users', function (Document $document, Request $request) { + $document->title = 'Users'; + }) +]; +``` diff --git a/docs/tr/extend/search.md b/docs/tr/extend/search.md index 4097860a6..6687f0222 100644 --- a/docs/tr/extend/search.md +++ b/docs/tr/extend/search.md @@ -1,3 +1,213 @@ -# Arama +# Searching and Filtering -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Flarum treats searching and filtering as parallel but distinct processes. Which process is used to handle a request to a [`List` API endpoint](/extend/data.html#api-endpoints) depends on the query parameters: + +- Filtering is applied when the `filter[q]` query param is omitted. Filters represent **structured** queries: for instance, you might want to only retrieve discussions in a certain category, or users who registered before a certain date. Filtering computes results based entirely on `filter[KEY] = VALUE` query parameters. +- Searching is applied when the `filter[q]` query param is included. Searches represent **unstructured** queries: the user submits an arbitrary string, and data records that "match" it are returned. For instance, you might want to search discussions based on the content of their posts, or users based on their username. Searching computes results based solely on parsing the `filter[q]` query param: all other `filter[KEY] = VALUE` params are ignored when searching. It's important to note that searches aren't entirely unstructured: the dataset being searched can be constrained by gambits (which are very similar to filters, and will be explained later). + +This distinction is important because searches and filters have very different use cases: filters represent *browsing*: that is, the user is passively looking through some category of content. In contrast, searches represent, well, *searching*: the user is actively looking for content based on some criteria. + +Flarum implements searching and filtering via per-model `Searcher` and `Filterer` classes (discussed in more detail below). Both classes accept a [`Flarum\Query\QueryCriteria`](https://api.docs.flarum.org/php/master/flarum/query/querycriteria) instance (a wrapper around the user and query params), and return a [`Flarum\Query\QueryResults`](https://api.docs.flarum.org/php/master/flarum/query/queryresults) instance (a wrapper around an Eloquent model collection). This common interface means that adding search/filter support to models is quite easy. + +One key advantage of this split is that it allows searching to be implemented via an external service, such as ElasticSearch. For larger communities, this can be significantly more performant and accurate. There isn't a dedicated extender for this yet, so for now, replacing the default Flarum search driver requires overriding the container bindings of `Searcher` classes. This is a highly advanced use case; if you're interested in doing this, please reach out on our [community forums](https://discuss.flarum.org/). + +Remember that the [JSON:API schema](https://jsonapi.org/format) is used for all API requests. + +:::tip Reuse Code + +Often, you might want to use the same class as both a `Filter` and a `Gambit` (both explained below). +Your classes can implement both interface; see Flarum core's [`UnreadFilterGambit`](https://github.com/flarum/core/blob/master/src/Discussion/Query/UnreadFilterGambit.php) for an example. + +::: + +:::tip Query Builder vs Eloquent Builder + +`Filter`s, `Gambit`s, filter mutators, and gambit mutators (all explained below) receive a "state" parameter, which wraps + +::: + +## Filtering + +Filtering constrains queries based on `Filters` (highlighted in code to avoid confusion with the process of filtering), which are classes that implement `Flarum\Filter\FilterInterface` and run depending on query parameters. After filtering is complete, a set of callbacks called "filter mutators" run for every filter request. + +When the `filter` method on a `Filterer` class is called, the following process takes place ([relevant code](https://github.com/flarum/core/blob/master/src/Filter/AbstractFilterer.php#L50-L93)): + +1. An Eloquent query builder instance for the model is obtained. This is provided by the per-model `{MODEL_NAME}Filterer` class's `getQuery()` method. +2. We loop through all `filter[KEY] = VALUE` query params. For each of these, any `Filter`s registered to the model whose `getFilterKey()` method matches the query param `KEY` is applied. `Filter`s can be negated by providing the query param as `filter[-KEY] = VALUE`. Whether or not a `Filter` is negated is passed to it as an argument: implementing negation is up to the `Filter`s. +3. [Sorting](https://jsonapi.org/format/#fetching-sorting), [pagination](https://jsonapi.org/format/#fetching-pagination) are applied. +4. Any "filter mutators" are applied. These are callbacks that receive the filter state (a wrapper around the query builder and current user) and filter criteria, and perform some arbitrary changes. All "filter mutators" run on any request. +5. We calculate if there are additional matching model instances beyond the query set we're returning for this request, and return this value along with the actual model data, wrapped in a `Flarum\Query\QueryResults` object. + +### Modify Filtering for an Existing Model + +Let's say you've added a `country` column to the User model, and would like to filter users by country. We'll need to define a custom `Filter`: + +```php +getQuery()->where('users.country', $negate ? '!=' : '=', $country); + } +} +``` + +Note that `FilterState` is a wrapper around the Eloquent builder's underlying Query builder and the current user. + +Also, let's pretend that for some reason, we want to omit any users that have a different country from the current user on ANY filter. +We can use a "filter mutator" for this: + +```php +getQuery()->where('users.country', $filterState->getActor()->country); + } +} +``` + +Now, all we need to do is register these via the Filter extender: + +```php + // Other extenders + (new Extend\Filter(UserFilterer::class)) + ->addFilter(CountryFilter::class) + ->addFilterMutator(OnlySameCountryFilterMutator::class), + // Other extenders +``` + +### Add Filtering to a New Model + +To filter a model that doesn't support filtering, you'll need to create a subclass of `Flarum/Filter/AbstractFilterer` for that model. +For an example, see core's [UserFilterer](https://github.com/flarum/core/blob/master/src/User/Filter/UserFilterer.php). + +Then, you'll need to use that filterer in your model's `List` controller. For an example, see core's [ListUsersController](https://github.com/flarum/core/blob/master/src/Api/Controller/ListUsersController.php#L93-L98). + +## Searching + +Searchng constrains queries by applying `Gambit`s, which are classes that implement `Flarum\Search\GambitInterface`, based on the `filter[q]` query param. +After searching is complete, a set of callbacks called "search mutators" run for every search request. + +When the `search` method on a `Searcher` class is called, the following process takes place ([relevant code](https://github.com/flarum/core/blob/master/src/Search/AbstractSearcher.php#L55-L79)): + +1. An Eloquent query builder instance for the model is obtained. This is provided by the per-model `{MODEL_NAME}Searcher` class's `getQuery()` method. +2. The `filter[q]` param is split by spaces into "tokens". Each token is matched against the model's registered `Gambit`s (each gambit has a `match` method). For any tokens that match a gambit, that gambit is applied, and the token is removed from the query string. Once all regular `Gambit`s have ran, all remaining unmatched tokens are passed to the model's `FullTextGambit`, which implements the actual searching logic. For example if searching discussions, in the `filter[q]` string `'author:1 hello is:hidden' world`, `author:1` and `is:hidden` would get matched by core's Author and Hidden gambits, and `'hello world'` (the remaining tokens) would be passed to the `DiscussionFulltextGambit`. +3. [Sorting](https://jsonapi.org/format/#fetching-sorting), [pagination](https://jsonapi.org/format/#fetching-pagination) are applied. +4. Any "search mutators" are applied. These are callbacks that receive the search state (a wrapper around the query builder and current user) and criteria, and perform some arbitrary changes. All "search mutators" run on any request. +5. We calculate if there are additional matching model instances beyond the query set we're returning for this request, and return this value along with the actual model data, wrapped in a `Flarum\Query\QueryResults` object. + +### Modify Searching for an Existing Model + +Let's reuse the "country" examples we used above, and see how we'd implement the same things for searching: + +```php +getQuery()->where('users.country', $negate ? '!=' : '=', $country); + } +} +``` + +:::warning No Spaces in Gambit Patterns! + +Flarum splits the `filter[q]` string into tokens by splitting it at spaces. +This means that your custom gambits can NOT use spaces as part of their pattern. + +::: + +:::tip AbstractRegexGambit + +All a gambit needs to do is implement `Flarum\Search\GambitInterface`, which receives the search state and a token. +It should return if this gambit applies for the given token, and if so, make whatever mutations are necessary to the +query builder accessible as `$searchState->getQuery()`. + +However, for most gambits, the `AbstractRegexGambit` abstract class (used above) should be used as a base class. +This makes it a lot simpler to match and apply gambits. + +::: + +Similarly, the search mutator we need is almost identical to the filter mutator from before: + +```php +getQuery()->where('users.country', $filterState->getActor()->country); + } +} +``` + +We can register these via the `SimpleFlarumSearch` extender (in the future, the `Search` extender will be used for registering custom search drivers): + +```php + // Other extenders + (new Extend\SimpleFlarumSearch(UserSearcher::class)) + ->addGambit(CountryGambit::class) + ->addSearchMutator(OnlySameCountrySearchMutator::class), + // Other extenders +``` + +### Add Searching to a New Model + +To support searching for a model, you'll need to create a subclass of `Flarum/Search/AbstractSearcher` for that model. +For an example, see core's [UserSearcher](https://github.com/flarum/core/blob/master/src/User/Search/UserSearcher.php). + +Then, you'll need to use that searcher in your model's `List` controller. For an example, see core's [ListUsersController](https://github.com/flarum/core/blob/master/src/Api/Controller/ListUsersController.php#L93-L98). + +Every searcher **must** have a fulltext gambit (the logic that actually does the searching). Otherwise, it won't be booted by Flarum, and you'll get an error. +See core's [FulltextGambit for users](https://github.com/flarum/core/blob/master/src/User/Search/Gambit/FulltextGambit.php) for an example. +You can set (or override) the full text gambit for a searcher via the `SimpleFlarumSearch` extender's `setFullTextGambit()` method. + +### Search Drivers + +Coming soon! + +## Frontend Tools + +Coming soon! diff --git a/docs/tr/extend/service-provider.md b/docs/tr/extend/service-provider.md index fc8b9028a..6399c29c1 100644 --- a/docs/tr/extend/service-provider.md +++ b/docs/tr/extend/service-provider.md @@ -1,3 +1,68 @@ -# Servis Sağlayıcı +# Service Provider -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +As noted throughout this documentation, Flarum uses [Laravel's service container](https://laravel.com/docs/8.x/container) (or IoC container) for dependency injection. +[Service Providers](https://laravel.com/docs/8.x/providers) allow low-level configuration and modification of the Flarum backend. +The most common use case for service providers is to create, modify, or replace container bindings. +That being said, service providers allow you full access to run whatever logic you need during application boot with access to the container. + +::: warning Advanced Use Only!!! + +Unlike with other extenders, the Service Provider layer is NOT use-case driven, and is NOT considered public API. It is subject to change at any time, without notice or deprecation. This should only be used if you know what you're doing, and the other extenders don't satisfy your use case. + +::: + +## Flarum Boot Process + +To understand service providers, you must first understand the order in which Flarum boots. Most of this happens in [Flarum\Foundation\InstalledSite](https://github.com/flarum/core/blob/master/src/Foundation/InstalledSite.php) + +1. The container and application are initialized, and essential bindings (config, environment, logger) are registered +2. The `register` methods of all core service providers are run. +3. The `extend` methods of all extenders used by all enabled extensions are run. +4. The `extend` methods of all extenders used in the Flarum site's local `extend.php` are run. +5. The `boot` methods of all core service providers are run. + +## Custom Service Providers + +A custom service provider should extend `Flarum\Foundation\AbstractServiceProvider`, and can have a `boot` and a `register` method. For example: + +```php +container->resolving(SomeClass::class, function ($container) { + return new SomeClass($container->make('some.binding')); + }) + } + + public function boot(Container $container) + { + // custom logic here + } +} +``` + +The `register` method will run during step (3) above, and the `boot` method will run during step (5) above. In the `register` method, the container is available via `$this->container`. In the `boot` method, the container (or any other arguments), should be injected via typehinted method arguments. + +Flarum does not currently support Laravel Octane, but some [best practices](https://laravel.com/docs/8.x/octane#dependency-injection-and-octane), like using the `$container` argument inside `bind`, `singleton`, and `resolving` callbacks instead of `$this->container` should be used. See the [Octane documentation](https://laravel.com/docs/8.x/octane#dependency-injection-and-octane) for more information. + +To actually register your custom service provider, you can use the `ServiceProvider` extender in `extend.php`: + +```php +register(CustomServiceProvider::class), + // Other extenders +]; +``` diff --git a/docs/tr/extend/settings.md b/docs/tr/extend/settings.md index d7b810b11..5e0f1b492 100644 --- a/docs/tr/extend/settings.md +++ b/docs/tr/extend/settings.md @@ -1,3 +1,93 @@ -# Ayarlar +# Settings -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +At some point while making an extension, you might want to read some of the forum's settings or store certain settings specific to your extension. Thankfully, Flarum makes this very easy. + +## The Settings Repository + +Reading or changing settings can be done using an implementation of the `SettingsRepositoryInterface`. +Because Flarum uses [Laravel's service container](https://laravel.com/docs/8.x/container) (or IoC container) for dependency injection, you don't need to worry about where to obtain such a repository, or how to instantiate one. +Instead, you can rely on the container to instantiate your class and inject the correct dependencies. + +```php +settings = $settings; + } +} +``` + +Great! Now the `SettingsRepositoryInterface` is available through `$this->settings` to our class. + +### Reading Settings + +To read settings, all we have to do is use the repository's `get()` function: + +`$this->settings->get('forum_title')` + +The `get()` function accepts two arguments: + +1. The name of the setting you are trying to read. +2. (Optional) A default value if no value has been stored for such a setting. By default, this will be `null`. + +### Storing Settings + +Storing settings ist just as easy, use the `set()` function: + +`$this->settings->set('forum_title', 'Super Awesome Forum')` + +The `set` function also accepts two arguments: + +1. The name of the setting you are trying to change. +2. The value you want to store for this setting. + +### Other Functions + +The `all()` function returns an array of all known settings. + +The `delete($name)` function lets you remove a named setting. + +## Settings in the Frontend + +### Editing Settings + +To learn more about adding settings through the admin dashboard, see the [relevant documentation](admin.md). +### Accessing Settings + +All settings are available in the `admin` frontend via the `app.data.settings` global. +However, this is not done in the `forum` frontend, as anyone can access it, and you wouldn't want to leak all your settings! (Seriously, that could be a very problematic data breach). + +Instead, if we want to use settings in the `forum` frontend, we'll need to serialize them and send them alongside the initial forum data payload. + +This can be done via the `Settings` extender. For example: + +**extend.php** + +```php +use Flarum\Extend; + +return [ + (new Extend\Settings) + ->serializeToForum('myCoolSetting', 'my.cool.setting.key') + ->serializeToForum('myCoolSettingModified', 'my.cool.setting.key', function ($retrievedValue) { + // This third argument is optional, and allows us to pass the retrieved setting through some custom logic. + // In this example, we'll append a string to it. + + return "My Cool Setting: $retrievedValue"; + }, "default value!"), +] +``` + +Now, the `my.cool.setting.key` setting will be accessible in the frontend as `app.forum.attribute("myCoolSetting")`, and our modified value will be accessible via `app.forum.attribute("myCoolSettingModified")`. diff --git a/docs/tr/extend/slugging.md b/docs/tr/extend/slugging.md index e9561eb0a..3dc39416b 100644 --- a/docs/tr/extend/slugging.md +++ b/docs/tr/extend/slugging.md @@ -1,3 +1 @@ -# Model Yavaşlatma - -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +# Model Slugging diff --git a/docs/tr/extend/start.md b/docs/tr/extend/start.md index 7e5cbe9f7..81dd8318e 100644 --- a/docs/tr/extend/start.md +++ b/docs/tr/extend/start.md @@ -1,3 +1,158 @@ -# Başlangıç +# Getting Started -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Want to build a Flarum extension? You've come to the right place! This document will take you through some essential concepts, after which you'll build your first Flarum extension from scratch. + +## Architecture + +In order to understand how to extend Flarum, first we need to understand a bit about how Flarum is built. + +Be aware that Flarum uses some _modern_ languages and tools. If you've only ever built WordPress plugins before, you might feel a bit out of your depth! That's OK — this is a great time to learn cool new things and extend your skillset. However, we would advise that you become somewhat familiar with the technologies described below before proceeding. + +Flarum is made up of three layers: + +* First, there is the **backend**. This is written in [object-oriented PHP](https://laracasts.com/series/object-oriented-bootcamp-in-php), and makes use of a wide array of [Laravel](https://laravel.com/) components and other packages via [Composer](https://getcomposer.org/). You'll also want to familiarize yourself with the concept of [Dependency Injection](https://laravel.com/docs/8.x/container), which is used throughout our backend. + +* Second, the backend exposes a **public API** which allows frontend clients to interface with your forum's data. This is built according to the [JSON:API specification](https://jsonapi.org/). + +* Finally, there is the default web interface which we call the **frontend**. This is a [single-page application](https://en.wikipedia.org/wiki/Single-page_application) which consumes the API. It's built with a simple React-like framework called [Mithril.js](https://mithril.js.org). + +Extensions will often need to interact with all three of these layers to make things happen. For example, if you wanted to build an extension that adds custom fields to user profiles, you would need to add the appropriate database structures in the **backend**, expose that data in the **public API**, and then display it and allow users to edit it on the **frontend**. + +So... how do we extend these layers? + +## Extenders + +In order to extend Flarum, we will be using a concept called **extenders**. Extenders are *declarative* objects that describe in plain terms the goals you are trying to achieve (such as adding a new route to your forum, or executing some code when a new discussion was created). + +Every extender is different. However, they will always look somewhat similar to this: + +```php +// Register a JavaScript and a CSS file to be delivered with the forum frontend +(new Extend\Frontend('forum')) + ->js(__DIR__.'/forum-scripts.js') + ->css(__DIR__.'/forum-styles.css') +``` + +You first create an instance of the extender, and then call methods on it for further configuration. All of these methods return the extender itself, so that you can achieve your entire configuration just by chaining method calls. + +To keep things consistent, we use this concept of extenders in both the backend (in PHP land) and the frontend (in JavaScript land). _Everything_ you do in your extension should be done via extenders, because they are a **guarantee** we are giving to you that a future minor release of Flarum won't break your extension. + +All of the extenders currently available to you from Flarum's core can be found in the [`Extend` namespace](https://github.com/flarum/core/blob/master/src/Extend) [(PHP API documentation)](https://api.docs.flarum.org/php/master/flarum/extend) Extensions may also offer their [own extenders](extensibility.md#custom-extenders). + +## Hello World + +Want to see an extender in action? The `extend.php` file in the root of your Flarum installation is the easiest way to register extenders just for your site. It should return an array of extender objects. Pop it open and add the following: + +```php +content(function (Document $document) { + $document->head[] = ''; + }) +]; +``` + +Now pay your forum a visit for a pleasant (albeit extremely obtrusive) greeting. 👋 + +For simple site-specific customizations – like adding a bit of custom CSS/JavaScript, or integrating with your site's authentication system – the `extend.php` file in your forum's root is great. But at some point, your customization might outgrow it. Or maybe you have wanted to build an extension to share with the community from the get-go. Time to build an extension! + +## Extension Packaging + +[Composer](https://getcomposer.org) is a dependency manager for PHP. It allows applications to easily pull in external code libraries and makes it easy to keep them up-to-date so that security and bug fixes are propagated rapidly. + +As it turns out, every Flarum extension is also a Composer package. That means someone's Flarum installation can "require" a certain extension and Composer will pull it in and keep it up-to-date. Nice! + +During development, you can work on your extensions locally and set up a [Composer path repository](https://getcomposer.org/doc/05-repositories.md#path) to install your local copy. Create a new `packages` folder in the root of your Flarum installation, and then run this command to tell Composer that it can find packages in here: + +```bash +composer config repositories.0 path "packages/*" +``` + +Now let's start building our first extension. Make a new folder inside `packages` for your extension called `hello-world`. We'll put two files in it: `extend.php` and `composer.json`. These files serve as the heart and soul of the extension. + +### extend.php + +The `extend.php` file is just like the one in the root of your site. It will return an array of extender objects that tell Flarum what you want to do. For now, just move over the `Frontend` extender that you had earlier. + +### composer.json + +We need to tell Composer a bit about our package, and we can do this by creating a `composer.json` file: + +```json +{ + "name": "acme/flarum-hello-world", + "description": "Say hello to the world!", + "type": "flarum-extension", + "require": { + "flarum/core": ">=0.1.0-beta.16 <=0.1.0" + }, + "autoload": { + "psr-4": {"Acme\\HelloWorld\\": "src/"} + }, + "extra": { + "flarum-extension": { + "title": "Hello World", + "icon": { + "name": "fas fa-smile", + "backgroundColor": "#238c59", + "color": "#fff" + } + } + } +} +``` + +* **name** is the name of the Composer package in the format `vendor/package`. + * You should choose a vendor name that’s unique to you — your GitHub username, for example. For the purposes of this tutorial, we’ll assume you’re using `acme` as your vendor name. + * You should prefix the `package` part with `flarum-` to indicate that it’s a package specifically intended for use with Flarum. + +* **description** is a short one-sentence description of what the extension does. + +* **type** MUST be set to `flarum-extension`. This ensures that when someone "requires" your extension, it will be identified as such. + +* **require** contains a list of your extension's own dependencies. + * You'll want to specify the version of Flarum that your extension is compatible with here. + * This is also the place to list other Composer libraries your code needs to work. + + ::: warning Carefully choose the Flarum version + + While Flarum is still in beta, we recommend that you declare compatibility only with the current version of Flarum: + + "flarum/core": ">=0.1.0-beta.16 <=0.1.0" + + ::: + +* **autoload** tells Composer where to find your extension's classes. The namespace in here should reflect your extensions' vendor and package name in CamelCase. + +* **extra.flarum-extension** contains some Flarum-specific information, like your extension's display name and how its icon should look. + * **title** is the display name of your extension. + * **icon** is an object which defines your extension's icon. The **name** property is a [Font Awesome icon class name](https://fontawesome.com/icons). All other properties are used as the `style` attribute for your extension's icon. + +See [the composer.json schema](https://getcomposer.org/doc/04-schema.md) documentation for information about other properties you can add to `composer.json`. + +::: tip + +Use the [FoF extension generator](https://github.com/FriendsOfFlarum/extension-generator) to automatically create your extension's scaffolding. + +::: + +### Installing Your Extension + +The final thing we need to do to get up and running is to install your extension. Navigate to the root directory of your Flarum install and run the following command: + +```bash +composer require acme/flarum-hello-world *@dev +``` + +Once that's done, go ahead and fire 'er up on your forum's Administration page, then navigate back to your forum. + +*whizzing, whirring, metal clunking* + +Woop! Hello to you too, extension! + +We're making good progress. We've learned how to set up our extension and use extenders, which opens up a lot of doors. Read on to learn how to extend Flarum's frontend. diff --git a/docs/tr/extend/testing.md b/docs/tr/extend/testing.md index 671ecb560..6892f4a1c 100644 --- a/docs/tr/extend/testing.md +++ b/docs/tr/extend/testing.md @@ -1,3 +1,370 @@ -# Deneme +# Testing -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Automated testing ensures that your extension performs as you expect it to, helps avoid introducing new bugs or regressions, and saves time on manual testing. +Flarum currently provides tooling for automated backend unit and integration tests, and we plan to release support for frontend unit testing and E2E testing in the future. + +## Backend Tests + +The `flarum/testing` library is used by core and some bundled extensions for automated unit and integration tests. +It is essentially a collection of utils that allow testing Flarum core and extensions with PHPUnit. + +### Setup + +Firstly, you will need to require the `flarum/testing` composer package as a dev dependency for your extension: + +`composer require --dev flarum/testing:^0.1.0-beta.16` + +Then, you will need to set up a file structure for tests, and add PHPUnit configuration: + +``` +tests +├── fixtures (put any files needed by your tests here (blade templates, images, etc)) +├── integration +│ ├── setup.php (code below) +│ └── PUT INTEGRATION TESTS HERE (organizing into folder is generally a good idea) +├── unit +│ ├── PUT UNIT TESTS HERE +├── phpunit.integration.xml (code below) +└── phpunit.unit.xml (code below) +``` + +#### phpunit.integration.xml + +This is just an example [phpunit config file](https://phpunit.readthedocs.io/en/9.3/configuration.html) for integration tests. You can tweak this as needed, but keep `backupGlobals`, `backupStaticAttributes`, and `processIsolation` unchanged. + +```xml + + + + + + + ./integration + + + +``` + +#### phpunit.unit.xml + +This is just an example [phpunit config file](https://phpunit.readthedocs.io/en/9.3/configuration.html) for unit tests. You can tweak this as needed. + +```xml + + + + + + + ./unit + + + + + + +``` + +#### setup.php + +This script will be run to set up a testing database / file structure. + +```php +run(); +``` + +#### composer.json Modifications + +We will also want to add scripts to our `composer.json`, so that we can run our test suite via `composer test`. Add some variant of the following to your `composer.json`: + +```json +"scripts": { + "test": [ + "@test:unit", + "@test:integration" + ], + "test:unit": "phpunit -c tests/phpunit.unit.xml", + "test:integration": "phpunit -c tests/phpunit.integration.xml", + "test:setup": "@php tests/integration/setup.php" +}, +"scripts-descriptions": { + "test": "Runs all tests.", + "test:unit": "Runs all unit tests.", + "test:integration": "Runs all integration tests.", + "test:setup": "Sets up a database for use with integration tests. Execute this only once." +} +``` + +#### Github Testing Workflow + +In public repos, Github Actions allow you to run all your tests on every commit and pull request for free. You can copy [Flarum core's Github Actions](https://github.com/flarum/core/blob/master/.github/workflows/test.yml) testing workflow into your extension's `.github/workflows` folder to enable this. + +--- + +Now that we have everything in place, we need to set up our testing site for integration tests. For this, we will need a MySQL or MariaDb instance, and a place to store testing files. + +Testing database information is configured via the `DB_HOST` (defaults to `localhost`), `DB_PORT` (defaults to `3306`), `DB_DATABASE` (defaults to `flarum_test`), `DB_USERNAME` (defaults to `root`), `DB_PASSWORD` (defaults to `root`), and `DB_PREFIX` (defaults to `''`) environmental variables. The testing tmp directory path is configured via the `FLARUM_TEST_TMP_DIR_LOCAL` or `FLARUM_TEST_TMP_DIR` environmental variables, with the former taking precedence over the latter. If neither are provided, a `tmp` directory will be created in the `vendor` folder of your extension's local install. + +Now that we've provided the needed information, all we need to do is run `composer test:setup` in our extension's root directory, and we have our testing environment ready to go! + +Since [(almost)](https://github.com/flarum/core/blob/master/tests/integration/api/discussions/ListWithFulltextSearchTest.php#L29-L43) all database operations in integration tests are run in transactions, developers working on multiple extensions will generally find it more convenient to use one shared database and tmp directory for testing all their extensions. To do this, set the database config and `FLARUM_TEST_TMP_DIR` environmental variables in your `.bashrc` or `.bash_profile` to the path you want to use, and run the setup script for any one extension (you'll still want to include the setup file in every repo for CI testing via Github Actions). You should then be good to go for any Flarum extension (or core). + +### Using Integration Tests + +Flarum's integration test utils are contained in the `Flarum\Testing\integration\TestCase` class. It: + +- Boots (and makes available) an instance of the Flarum application. +- Allows pre-populating the database, enabling extensions, and adding extenders. +- Runs all database changes in transactions, so your test database retains the default post-installation state. +- Allows sending requests through the middleware stack to test HTTP endpoints. + +Your testcase classes should extend this class. + +#### Test Case Setup + +There are several important utilities available for your test cases: + +- The `setting($key, $value)` method allows you to override settings before the app has booted. This is useful if your boot process has logic depending on settings (e.g. which driver to use for some system). +- Similarly, the `config($key, $value)` method allows you to override config.php values before the app has booted. You can use dot-delimited keys to set deep-nested values in the config array. +- The `extension($extensionId)` method will take Flarum IDs of extensions to enable as arguments. Your extension should always call this with your extension's ID at the start of test cases, unless the goal of the test case in question is to confirm some behavior present without your extension, and compare that to behavior when your extension is enabled. If your extension is dependent on other extensions, make sure they are included in the composer.json `require` field (or `require-dev` for [optional dependencies](dependencies.md)), and also list their composer package names when calling `extension()`. Note that you must list them in a valid order. +- The `extend($extender)` method takes instances of extenders as arguments, and is useful for testing extenders introduced by your extension for other extensions to use. +- The `prepareDatabase()` method allow you to pre-populate your database. This could include adding users, discussions, posts, configuring permissions, etc. Its argument is an associative array that maps table names to arrays of [record arrays](https://laravel.com/docs/8.x/queries#insert-statements). + +If your test case needs users beyond the default admin user, you can use the `$this->normalUser()` method of the `Flarum\Testing\integration\RetrievesAuthorizedUsers` trait. + +:::warning + +The `TestCase` class will boot a Flarum instance the first time its `app()` method is called. Any uses of `prepareDatabase`, `extend`, or `extension` after this happens will have no effect. Make sure you have done all the setup you need in your test case before calling `app()`, or `database()`, `server()`, or `send()`, which call `app()` implicitly. If you need to make database modifications after the app has booted, you can use the regular Eloquent save method, or the `Illuminate\Database\ConnectionInterface` instance obtained via calling the `database()` method. + +::: + +Of course, since this is all based on PHPUnit, you can use the `setUp()` methods of your test classes for common setup tasks. + +For example: + +```php +setting('my.custom.setting', true); + + // Let's assume our extension depends on tags. + // Note that tags will need to be in your extension's composer.json's `require-dev`. + // Also, make sure you include the ID of the extension currently being tested, unless you're + // testing the baseline without your extension. + $this->extension('flarum-tags', 'my-cool-extension'); + + // Note that this input isn't validated: make sure you're populating with valid, representative data. + $this->prepareDatabase([ + 'users' => [ + $this->normalUser() // Available for convenience. + ], + 'discussions' => [ + ['id' => 1, 'title' => 'some title', 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 1, 'first_post_id' => 1, 'comment_count' => 1] + ], + 'posts' => [ + ['id' => 1, 'number' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now(), 'user_id' => 1, 'type' => 'comment', 'content' => '

something

'] + ] + ]); + + // Most test cases won't need to test extenders, but if you want to, you can. + $this->extend((new CoolExtensionExtender)->doSomething('hello world')); + } + + /** + * @test + */ + public function some_phpunit_test_case() + { + // ... + } + + // ... +} +``` + +#### Sending Requests + +A common application of automated testing is pinging various HTTP endpoints with various data, authenticated as different users. +You can use this to ensure that: + +- Users can't access content they're not supported to access. +- Permission-based create/edit/delete operations perform as expected. +- The type and schema of data returned is correct. +- Some desired side effect is applied when pinging an API. +- The basic API operations needed by your extension aren't erroring, and don't break when you make changes. + +`TestCase` provides several utilities: + +- The `request()` method constructs a `Psr\Http\Message\ServerRequestInterface` implementing object from a path, a method, and some options, which can be used for authentication, attaching cookies, or configuring the JSON request body. See the [method docblock](https://github.com/flarum/testing/blob/main/src/integration/TestCase.php) for more information on available options. +- Once you've created a request instance, you can send it (and get a response object back) via the `send()` method. + +For example: + + + +```php +send( + $this->request('GET', '/api/users', ['authenticatedAs' => 1]) + ->withQueryParams(['filter' => ['q' => 'john group:1'], 'sort' => 'username']) + ); + + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @test + */ + public function can_create_user() + { + $response = $this->send( + $this->request( + 'POST', + '/api/users', + [ + 'authenticatedAs' => 1, + 'json' => [ + 'data' => [ + 'attributes' => [ + 'username' => 'test', + 'password' => 'too-obscure', + 'email' => 'test@machine.local' + ] + ] + ] + ] + ) + ); + + $this->assertEquals(200, $response->getStatusCode()); + } + + // ... +} +``` + +::: warning + +If you want to send query parameters in a GET request, you can't include them in the path; you'll need to add them afterwards with the `withQueryParams` method. + +::: + +::: warning + +This is an extreme edge case, but note that MySQL does not update the fulltext index in transactions, so the standard approach won't work if you're trying to test a modified fulltext query. See [core's approach](https://github.com/flarum/core/blob/master/tests/integration/extenders/SimpleFlarumSearchTest.php) for an example of a workaround. + +::: + +#### Console Tests + +If you want to test custom console commands, you can extend `Flarum\Testing\integration\ConsoleTestCase` (which itself extends the regular `Flarum\Testing\integration\TestCase`). It provides 2 useful methods: + +- `$this->console()` returns an instance of `Symfony\Component\Console\Application` +- `$this->runCommand()` takes an array that will be wrapped in `Symfony\Component\Console\Input\ArrayInput`, and run. See the [Symfony code docblock](https://github.com/symfony/console/blob/5.x/Input/ArrayInput.php#L22) for more information. + +For example: + +```php + 'some:command', // The command name, equivalent of `php flarum some:command` + 'foo' => 'bar', // arguments + '--lorem' => 'ipsum' // options + ]; + + $this->assertEquals('Some Output.', $this->runCommand($input)); + } +} +``` + +### Using Unit Tests + +TODO + +## Frontend Tests + +Coming Soon! + +## E2E Tests + +Coming Soon! diff --git a/docs/tr/extend/theme.md b/docs/tr/extend/theme.md index d4e641362..bd5071d96 100644 --- a/docs/tr/extend/theme.md +++ b/docs/tr/extend/theme.md @@ -1,3 +1,28 @@ -# Hızlı Başlangıç +# Themes -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Flarum "themes" are just extensions. Typically, you'll want to use the `Frontend` extender to register custom [Less](https://lesscss.org/#overview) and JS. +Of course, you can use other extenders too: for example, you might want to support settings to allow configuring your theme. + +You can indicate that your extension is a theme by setting the "extra.flarum-extension.category" key to "theme". For example: + +```jsonc{4} +{ + // other fields + "extra": { + "flarum-extension": { + "category": "theme" + } + } + // other fields +} +``` + +All this will do is show your extension in the "theme" section in the admin dashboard extension list. + +## Less Variable Customization + +You can define new Less variables in your extension's Less files. There currently isn't an extender to modify Less variable values in the PHP layer, but this is planned for future releases. + +## Switching Between Themes + +Flarum doesn't currently have a comprehensive system that would support switching between themes. This is planned for future releases. diff --git a/docs/tr/extend/update-1.0.md b/docs/tr/extend/update-1.0.md new file mode 100644 index 000000000..6d4058f25 --- /dev/null +++ b/docs/tr/extend/update-1.0.md @@ -0,0 +1,261 @@ +# Updating For 1.0 + +Flarum version 1.0 is the long-awaited stable release! This release brings a number of refactors, cleanup, and small improvements that should make your Flarum experience just a bit better! + +::: tip + +If you need help applying these changes or using new features, please start a discussion on the [community forum](https://discuss.flarum.org/t/extensibility) or [Discord chat](https://flarum.org/discord/). + +::: + +## Full Stack + +### Translations and transChoice + +#### Background + +Historically, Flarum has used Symfony for backend translations, and a port for frontend translations to keep format consistent. There are a few limitations to this approach though: + +- Developers need to decide between using `trans` or `transChoice` for pluralization +- The pluralization format is proprietary to Symfony +- We have to maintain the JS port ourselves +- Keys for values provided to backend translations need to be wrapped in curly braces. (e.g. `$this->translator->trans('some.translation', ['{username}' => 'Some Username!'])`). +- There's no support for complex applications (nested pluralization, non-number-based selection) +- As a result of the previous point, genderization is impossible. And that's kinda important for a lot of languages. + +### New System + +In v5, Symfony dropped their proprietary `transChoice` system in favor of the more-or-less standard [ICU MessageFormat](https://symfony.com/doc/5.2/translation/message_format.html). This solves pretty much every single one of the aforementioned issues. In this release, Flarum will be fully switching to ICU MessageFormat as well. What does this mean for extensions? + +- `transChoice` should not be used at all; instead, the variable passed for pluralization should be included in the data. +- Keys for backend translations no longer need to be surrounded by curly braces. +- Translations can now use the [`select` and `plural`](https://symfony.com/doc/5.2/translation/message_format.html) formatter syntaxes. For the `plural` formatter, the `offset` parameter and `#` magic variables are supported. +- These `select` and `plural` syntaxes can be nested to arbitrary depth. This is often a bad idea though (beyond, say, 2 levels), as things can get unnecessarily complex. + +No change to translation file naming is necessary (Symfony docs say that an `+intl-icu` suffix is necessary, but Flarum will now interpret all translation files as internationalized). + +#### Future Changes + +In the future, this will serve as a basis for additional features: + +- Translator preprocessors will allow extensions to modify arguments passed to translations. This will enable genderization (extensions could automatically extract a gender field from any objects of type "user" passed in). +- We could support internationalized "magic variables" for numbers: currently, `one` is supported, but others (`few`, `many`, etc) currently aren't. +- We could support ordinal formatting in additional to just plural formatting. + +#### Changes Needed in Extensions + +The `transChoice` methods in the frontend and backend have been removed. +The `trans` method should always be used for translating, regardless of pluralization. +If a translation requires pluralization, make sure you pass in the controlling variable as one of the arguments. + +In the frontend, code that looked like this: + +```js +app.translator.transChoice('some-translation', guestCount, {host: hostName}); +``` + +should be changed to: + +```js +// This uses ES6 key-property shorthand notation. {guestCount: guestCount} is equivalent to {guestCount} +app.translator.trans('some-translation', {host: hostName, guestCount }); +``` + +Similarly, in the backend, + +```php +$translator->transChoice('some-translation', $guestCount, ['{host}' => $hostName]); +``` + +should be changed to: + +```php +$translator->trans('some-translation', ['host' => $hostName, 'guestCount' => $guestCount]); +``` + +Note that in the backend, translation keys were previously wrapped in curly braces. +This is no longer needed. + +#### Changes Needed in Translations + +Translations that aren't using pluralization don't need any changes. + +Pluralized translations should be changed as follows: + +`For {count} minute|For {count} minutes` + +to + +`{count, plural, one {For # minute} other {For # minutes}}` + +Note that in this example, `count` is the variable that controls pluralization. If a different variable were used (such as guestCount in the example above), this would look like: + +`{guestCount, plural, one {For # minute} other {For # minutes}}` + +See [our i18n docs](i18n.md) for more information. + +### Permissions Changes + +For a long time, the `viewDiscussions` and `viewUserList` permissions have been confusing. Despite their names: + +- `viewDiscussions` controls viewing both discussions and users. +- `viewUserList` controls searching users, not viewing user profiles. + +To clear this up, in v1.0, these permissions have been renamed to `viewForum` and `searchUsers` respectively. +A migration in core will automatically adjust all permissions in the database to use the new naming. However, any extension code using the old name must switch to the new ones immediately to avoid security issues. To help the transfer, a warning will be thrown if the old permissions are referenced. + +We have also slightly improved tag scoping for permissions. Currently, permissions can be applied to tags if: + +- The permission is `viewForum` +- The permission is `startDiscussion` +- The permission starts with `discussion.` + +However, this doesn't work for namespaced permissions (`flarum-acme.discussion.bookmark`), or permissions that don't really have anything to do with discussions, but should still be scoped (e.g. `viewTag`). To counter this, a `tagScoped` attribute can be used on the object passed to [`registerPermission`](admin.md) to explicitly indicate whether the permission should be tag scopable. If this attribute is not provided, the current rules will be used to determine whether the permission should be tag scopable. + +## Frontend + +### Tooltip Changes + +The `flarum/common/components/Tooltip` component has been introduced as a simpler and less framework-dependent way to add tooltips. If your code is creating tooltips directly (e.g. via `$.tooltip()` in `oncreate` methods), you should wrap your components in the `Tooltip` component instead. For example: + +```tsx + + + +``` + +See [the source code](https://github.com/flarum/core/blob/master/js/src/common/components/Tooltip.tsx) for more examples and instructions. + +See [the PR](https://github.com/flarum/core/pull/2843/files) for examples of how to change existing code to use tooltips. + +### PaginatedListState + +The `flarum/common/states/PaginatedListState` state class has been introduced to abstract away most of the logic of `DiscussionListState` and `NotificationListState`. It provides support for loading and displaying paginated lists of JSON:API resources (usually models). In future releases, we will also provide an `PaginatedList` (or `InfiniteScroll`) component that can be used as a base class for these paginated lists. + +Please see [the source code](https://github.com/flarum/core/blob/master/js/src/common/states/PaginatedListState.ts) for a list of methods. + +Note that `flarum/forum/states/DiscussionListState`'s `empty` and `hasDiscussions` methods have been removed, and replaced with `isEmpty` and `hasItems` respectively. This is a breaking change. + +### New Loading Spinner + +The old `spin.js` based loading indicator has been replaced with a CSS-based solution. For the most part, no changes should be needed in extensions, but in some cases, you might need to update your spinner. This change also makes it easier to customize the spinner. + +See [this discussion](https://discuss.flarum.org/d/26994-beta16-using-the-new-loading-spinner) for more information. + +### classList util + +Ever wrote messy code trying to put together a list of classes for some component? Well, no longer! The [clsx library](https://www.npmjs.com/package/clsx) is now available as the `flarum/common/utils/classList` util. + +### User List + +An extensible user list has been added to the admin dashboard. In future releases, we hope to extract a generic model table component that can be used to list any model in the admin dashboard. + +See [the source code](https://github.com/flarum/core/blob/master/js/src/admin/components/UserListPage.tsx#L41) for a list of methods to extend, and examples of how columns should look like (can be added by extending the `columns` method and adding items to the [ItemList](frontend.md)). + +### Miscellaneous + +- Components should now call `super` for ALL Mithril lifecycle methods they define. Before, this was only needed for `oninit`, `onbeforeupdate`, and `oncreate`. Now, it is also needed in `onupdate`, `onbeforeremove`, and `onremove`. See [this GitHub issue](https://github.com/flarum/core/issues/2446) for information on why this change was made. +- The `flarum/common/utils/insertText` and `flarum/common/utils/styleSelectedText` utils have been moved to core from `flarum/markdown`. See `flarum/markdown` for an example of usage. +- The `extend` and `override` utils can now modify several methods at once by passing in an array of method names instead of a single method name string as the second argument. This is useful for extending the `oncreate` and `onupdate` methods at once. +- The `EditUserModal` component is no longer available through the `flarum/forum` namespace, it has been moved to `flarum/admin`. Imports should be adjusted. +- The `Model` and `Route` JS extenders have been removed for now. There aren't currently used in any extensions that we know of. We will be reintroducing JS extenders during v1.x releases. +- The `Search` component can now be used with the `SearchState` state class. Previously, `SearchState` was missing the `getInitialSearch` method expected by the `Search` component. + +## Backend + +### Filesystem Extenders + +In this release, we refactored our use of the filesystem to more consistently use [Laravel's filesystem API](https://laravel.com/docs/8.x/filesystem). Extensions can now declare new disks, more easily use core's `flarum-assets` and `flarum-avatars` disks, and create their own storage drivers, enabling CDN and cloud storage support. + +### Compat and Closure Extenders + +In early Flarum versions, the `extend.php` file allowed arbitrary functions that allowed exection of arbitrary code one extension boot. For example: + +```php +return [ + // other extenders + function (Dispatcher $events) { + $events->subscribe(Listener\FilterDiscussionListByTags::class); + $events->subscribe(Listener\FilterPostsQueryByTag::class); + $events->subscribe(Listener\UpdateTagMetadata::class); + } +]; +``` + +This approach was difficult to maintain and provide a well-tested public API for, frequently resolved classes early (breaking all sorts of things), and was not very descriptive. With the extender API completed in beta 16, this approach is no longer necessary. Support for these closures has been removed in this stable version. + +One type of functionality for which the extender replacement isn't obvious is container bindings ([e.g. flarum/pusher](https://github.com/flarum/pusher/blob/v0.1.0-beta.14/extend.php#L33-L49)). This can be done with via the service provider extender (e.g. [a newer version of flarum/pusher](https://github.com/flarum/pusher/blob/master/extend.php#L40-L41)). + +If you are unsure about which extenders should be used to replace your use of callbacks in `extend.php`, or are not sure that such an extender exists, please comment so below or reach out! We're in the final stages of finishing up the extender API, so now is the time to comment. + +### Scheduled Commands + +The [fof/console](https://github.com/FriendsOfFlarum/console) library has been a popular way to schedule commands (e.g. for publishing scheduled posts, running DB-heavy operations, etc) for several release. In Flarum 1.0, this functionality has been brought into core's `Console` extender. See our [console extension documentation](console.md) for more information on how to create schedule commands, and our [console user documentation](../console.md) for more information on how to run scheduled commands. + +### Eager Loading Extender + +As part of solving [N+1 Query issues](https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/) in some [Flarum API endpoints](https://github.com/flarum/core/issues/2637), we have introduced a `load` method on the `ApiController` extender that allows you to indicate relations that should be eager loaded. + +This should be done if you know a relation will always be included, or will always be referenced by controller / permission logic. For example, we will always need the tags of a discussion to check what permissions a user has on that discussion, so we should eager load the discussion's `tags` relationship. For example: + +```php +return [ + // other extenders + (new Extend\ApiController(FlarumController\ListDiscussionsController::class)) + ->addInclude(['tags', 'tags.state', 'tags.parent']) + ->load('tags'), +]; +``` + +### RequestUtil + +The `Flarum\Http\RequestUtil`'s `getActor` and `withActor` should be used for getting/setting the actor (user) on requests. `$request->getAttribute('actor')` and `$request->withAttribute('actor')` are deprecated, and will be removed in v2.0. + +### Miscellaneous + +- The `Formatter` extender now has an `unparse` method that allows modifying XML before unparsing content. +- All route names must now be unique. In beta 16, uniqueness was enforced per-method; now, it is mandatory for all routes. +- API requests sent through `Flarum\Api\Client` now run through middleware, including `ThrottleApi`. This means that it is now possible to throttle login/registration calls. +- In beta 16, registering custom [searchers](search.md) was broken. It has been fixed in stable. +- The `post_likes` table in the [flarum/likes](https://github.com/flarum/likes) extension now logs the timestamp when likes were created. This isn't used in the extension, but could be used in other extensions for analytics. +- The `help` attribute of [admin settings](admin.md) no longer disappears on click. +- The `generate:migration` console command has been removed. The [Flarum CLI](https://discuss.flarum.org/d/26525-rfc-flarum-cli-alpha) should be used instead. +- The `GambitManager` util class is now considered internal API, and should not be used directly by extensions. +- The `session` attribute is no longer available on the `User` class. This caused issues with queue drivers, and was not conceptually correct (a user can have multiple sessions). The current session is still available via the `$request` instance. +- The `app`, `base_path`, `public_path`, `storage_path`, and `event` global helpers have been restored, but deprecated perpetually. These exist in case Laravel packages need them; they **should not** be used directly by Flarum extension code. The `flarum/laravel-helpers` package has been abandoned. +- The following deprecated features from beta 16 have been removed: + - The `TextEditor`, `TextEditorButton`, and `SuperTextarea` components are no longer exported through the `flarum/forum` namespace, but through `flarum/common` (with the exception of `SuperTextarea`, which has been replaced with `flarum/common/utils/BasicEditorDriver`). + - Support for `Symfony\Component\Translation\TranslatorInterface` has been removed, `Symfony\Contracts\Translation\TranslatorInterface` should be used instead. + - All backwards compatibility layers for the [beta 16 access token refactors](update-b16.md#access-token-and-authentication-changes) have been removed + - The `GetModelIsPrivate` event has been removed. The `ModelPrivate` extender should be used instead. + - The `Searching` (for both `User` and `Discussion` models), `ConfigureAbstractGambits`, `ConfigureDiscussionGambits`, and `ConfigureUserGambits` events have been removed. The `SimpleFlarumSearch` extender should be used instead. + - The `ConfigurePostsQuery` event has been removed. The `Filter` extender should be used instead. + - The `ApiSerializer` extender's `mutate` method has been removed. The same class's `attributes` method should be used instead. + - The `SearchCriteria` and `SearchResults` util classes have been removed. `Flarum\Query\QueryCriteria` and `Flarum\Query\QueryResults` should be used instead. + - The `pattern` property of `AbstractRegexGambit` has been removed; the `getGambitPattern` method is now a required abstract method. + - The `AbstractSearch` util class has been removed. `Flarum\Search\SearchState` and `Flarum\Filter\FilterState` should be used instead. + - The `CheckingPassword` event has been removed, the `Auth` extender should be used instead. + +## Tags Extension Changes + +As mentioned above, [tag scopable permissions](#permissions-changes) can now be explicitly declared. + +We've also made several big refactors to significantly improve tags performance. + +Firstly, the [Tag](https://github.com/flarum/tags/blob/d093ca777ba81f826157522c96680717d3a90e24/src/Tag.php#L38-L38) model's static `getIdsWhereCan` and `getIdsWhereCannot` methods have been removed. These methods scaled horribly on forums with many tags, sometimes adding several seconds to response time. + +These methods have been replaced with a `whereHasPermission` [Eloquent dynamic scope](https://laravel.com/docs/8.x/eloquent#dynamic-scopes) that returns a query. For example: + +`Tag::whereHasPermission($actor, 'viewDiscussions)`. + +That query can then be used in other DB queries, or further constricted to retrieve individual tags. + +Secondly, we no longer load in all tags as part of the initial payload. The initial payload contains all top-level primary tags, and the top 3 secondary tags. Other tags are loaded in as needed. Future releases will paginate secondary tags. This should enable forums to have thousands of secondary tags without significant performance impacts. These changes mean that extensions should not assume that all tags are available in the model store. + +## Testing Library Changes + +- Bundled extensions will not be automatically enabled; all enabled extensions must be specified in that test case. +- `setting` and `config` methods have been added that allow configuring settings and config.php values before the tested application boots. See [the testing docs](testing.md) for more information. +- The `php flarum test:setup` command will now drop the existing test DB tables before creating the database. This means that you can run `php flarum test:setup` to ensure a clean database without needing to go into the database and drop tables manually. diff --git a/docs/tr/extend/update-b10.md b/docs/tr/extend/update-b10.md index 8d7b48b89..bbbd4d453 100644 --- a/docs/tr/extend/update-b10.md +++ b/docs/tr/extend/update-b10.md @@ -1,3 +1,53 @@ -# Beta 10 için Güncelleme +# Updating For Beta 10 -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Beta 10 further solidifies the core architecture, offering new extenders as a stable, use-case-driven API for extending Flarum's core. A few small changes may necessitate updates to your extensions to make them compatible with Beta 10. These are detailed below. + +::: tip + +If you need help applying these changes or using new features, please start a discussion on the [community forum](https://discuss.flarum.org/t/extensibility) or [Discord chat](https://flarum.org/discord/). + +::: + +## Breaking Changes + +- The `Flarum\Event\GetDisplayName` class has been moved to `Flarum\User\Event\GetDisplayName`. +- The `Flarum\Http\Exception\ForbiddenException` has been removed. Use `Flarum\User\Exception\PermissionDeniedException` instead. +- The `assertGuest()` method of the `Flarum\User\AssertPermissionTrait` has been removed without replacement. +- Old error handling middleware and exception handler classes were removed (see "New Features" for more details): + - `Flarum\Api\Middleware\HandleErrors` + - `Flarum\Http\Middlware\HandleErrorsWithView` + - `Flarum\Http\Middlware\HandleErrorsWithWhoops` + - `Flarum\Api\ErrorHandler` + - `Flarum\Api\ExceptionHandler\FallbackExceptionHandler` + - `Flarum\Api\ExceptionHandler\FloodingExceptionHandler` + - `Flarum\Api\ExceptionHandler\IlluminateValidationExceptionHandler` + - `Flarum\Api\ExceptionHandler\InvalidAccessTokenExceptionHandler` + - `Flarum\Api\ExceptionHandler\InvalidConfirmationTokenExceptionHandler` + - `Flarum\Api\ExceptionHandler\MethodNotAllowedExceptionHandler` + - `Flarum\Api\ExceptionHandler\ModelNotFoundExceptionHandler` + - `Flarum\Api\ExceptionHandler\PermissionDeniedExceptionHandler` + - `Flarum\Api\ExceptionHandler\RouteNotFoundExceptionHandler` + - `Flarum\Api\ExceptionHandler\TokenMismatchExceptionHandler` + - `Flarum\Api\ExceptionHandler\ValidationExceptionHandler` + - `Flarum\Api\ExceptionHandler\FallbackExceptionHandler` + - `Flarum\Api\ExceptionHandler\FallbackExceptionHandler` + +## Recommendations + +- We tweaked the [recommended flarum/core version constraints for extensions](start.md#composer-json). We now recommend you mark your extension as compatible with the current and the upcoming beta release. (For beta.10, that would be any beta.10.x and beta.11.x version.) The core team will strive to make this work well by deprecating features before removing them. More details on this change in [this pull request](https://github.com/flarum/docs/pull/75). + +## New Features + +- New, extensible **error handling** stack in the `Flarum\Foundation\ErrorHandling` namespace: The `Registry` maps exceptions to "types" and HTTP status codes, `HttpFormatter` instances turn them into HTTP responses. Finally, `Reporter` instances are notified about unknown exceptions. + - You can build custom exception classes that will abort the current request (or console command). If they have semantic meaning to your application, they should implement the `Flarum\Foundation\KnownError` interface, which exposes a "type" that is used to render pretty error pages or dedicated error messages. + - More consistent use of HTTP 401 and 403 status codes. HTTP 401 should be used when logging in (i.e. authenticating) could make a difference; HTTP 403 is reserved for requests that fail because the already authenticated user is lacking permissions to do something. + - The `assertRegistered()` and `assertPermission()` methods of the `Flarum\User\AssertPermissionTrait` trait have been changed to match above semantics. See [this pull request](https://github.com/flarum/core/pull/1854) for more details. + - Error views are now determined based on error "type", not on status code (see [bdac88b](https://github.com/flarum/core/commit/bdac88b5733643b9c5dabae9e09a64d9f6e41d58)) +- **Queue support**: This release incorporates Laravel's illuminate/queue package, which allows offloading long-running tasks (such as email sending or regular cleanup jobs) onto a dedicated worker process. These changes are mostly under the hood, the next release(s) will start using the queue system for sending emails. By default, Flarum will use the "sync" queue driver, which executes queued tasks immediately. This is far from ideal and mostly guarantees a hassle-free setups. Production-grade Flarum installs are expected to upgrade to a more full-featured queue adapter. +- The `Flarum\Extend\LanguagePack` now accepts an optional path in its constructor. That way, language packs can store their locales in a different directory if they want to. +- The `formatContent()` method of `Flarum\Post\CommentPost` can now be called without an HTTP request instance, e.g. when rendering a post in an email template. + +## Deprecations + +- **Reminder**: In previous versions of Flarum, an extensions' main file was named `bootstrap.php`. This name will no longer be supported in the stable 0.1 release. Make sure your extension uses the name `extend.php`. +- Laravel's global string and array helpers (e.g. `str_contains()` and `array_only()`) are deprecated in favor of their class based alternatives (`Illuminate\Support\Str::contains()` and `Illuminate\Support\Arr::only()`). See the [announcement](https://laravel-news.com/laravel-5-8-deprecates-string-and-array-helpers) and [pull request](https://github.com/laravel/framework/pull/26898) for more information. diff --git a/docs/tr/extend/update-b12.md b/docs/tr/extend/update-b12.md index eed777c3f..6cbcdba03 100644 --- a/docs/tr/extend/update-b12.md +++ b/docs/tr/extend/update-b12.md @@ -1,3 +1,35 @@ -# Beta 12 için Güncelleme +# Updating For Beta 12 -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Beta 12 packs several new features for extension developers, but also continues our cleanup efforts which results in a few changes, so please read this guide carefully to find out whether + your extensions are affected. We invested extra effort to introduce new functionality in a backward-compatible manner or first deprecate functionality before it will be removed in the next release, in line with our [versioning recommendations](start.md#composer-json). + +::: tip + +If you need help applying these changes or using new features, please start a discussion on the [community forum](https://discuss.flarum.org/t/extensibility) or [Discord chat](https://flarum.org/discord/). + +::: + +## Deprecations / Upcoming breaking changes + +- **Reminder**: In previous versions of Flarum, an extensions' main file was named `bootstrap.php`. This name will no longer be supported in the stable 0.1 release. Make sure your extension uses the name `extend.php`. +- PHP 7.1 support will be dropped in beta.13. +- Using library classes from the `Zend` namespace is now deprecated and will be removed in beta.13. Use the `Laminas` namespace instead. See [PR #1963](https://github.com/flarum/core/pull/1963). +- The `Flarum\Util\Str::slug()` method has been deprecated. Use `Illuminate\Support\Str::slug()` instead. +- The `Flarum\Event\ConfigureMiddleware` has been deprecated. We finally have a [proper replacement](middleware.md) - see "New Features" below. Therefore, it will be removed in beta.13. +- If you implement the `Flarum\Mail\DriverInterface`: + - Returning a plain array of field names from the `availableSettings()` method is deprecated, but still supported. It must now return an array of field names mapping to their type. See [the inline documentation](https://github.com/flarum/core/blob/08e40bc693cce7be02d4fb24633553c7eaf2738d/src/Mail/DriverInterface.php#L25-L32) for more details. + - Implement the `validate()` method that will be required in beta.13. See [its documentation](https://github.com/flarum/core/blob/08e40bc693cce7be02d4fb24633553c7eaf2738d/src/Mail/DriverInterface.php#L34-L48). + - Implement the `canSend()` method that will be required in beta.13. See [its documentation](https://github.com/flarum/core/blob/08e40bc693cce7be02d4fb24633553c7eaf2738d/src/Mail/DriverInterface.php#L50-L54). + +## New Features + +- New **PHP extenders**: + - `Flarum\Extend\Middleware` offers methods for adding, removing or replacing middleware in our three middleware stacks (api, forum, admin). We also added [documentation](middleware.md) for this feature. + - `Flarum\Extend\ErrorHandling` lets you configure status codes and other aspects of our error handling stack depending on error types or exception classes. +- **JavaScript**: + - The `flarum/components/Select` component now supports a `disabled` prop. See [PR #1978](https://github.com/flarum/core/pull/1978). + +## Other changes / Recommendations + +- The `TextFormatter` library has been updated to (at least) version 2.3.6. If you are using it (likely through our own `Flarum\Formatter\Formatter` class), we recommend scanning [the library's changelog](https://github.com/s9e/TextFormatter/blob/2.3.6/CHANGELOG.md). +- The JS `slug()` helper from the `flarum/utils/string` module should only be used to *suggest* slugs to users, not enforce them. It does not employ any sophisticated transliteration logic like its PHP counterpart. diff --git a/docs/tr/extend/update-b13.md b/docs/tr/extend/update-b13.md index 754386ca5..fc6f00d82 100644 --- a/docs/tr/extend/update-b13.md +++ b/docs/tr/extend/update-b13.md @@ -1,3 +1,40 @@ -# Beta 13 için Güncelleme +# Updating For Beta 13 -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Beta 13 ships with several new extenders to simplify building and maintaining extensions. We do our best to create backward compatibility changes. We recommend changing to new Extenders as soon as they are available. + +::: tip + +If you need help applying these changes or using new features, please start a discussion on the [community forum](https://discuss.flarum.org/t/extensibility) or [Discord chat](https://flarum.org/discord/). + +::: + +## Breaking Changes + +- Dropped support for PHP 7.1. +- Classes from the `Zend` namespace are now removed. Use the `Laminas` namespace instead. See [PR #1963](https://github.com/flarum/core/pull/1963). +- The `Flarum\Util\Str::slug()` method has been removed including the class. Use `Illuminate\Support\Str::slug()` instead. +- The `Flarum\Event\ConfigureMiddleware` has been removed. Use the [proper replacement](middleware.md). +- Several events used in Event Listeners have been removed, use their [replacement extender](start.md#extenders) instead. +- The LanguagePack extender only loads keys from extensions that are enabled. The translations loaded are based on the yaml files matching the [i18n namespace](i18n.md#appendix-a-standard-key-format). +- All notifications are now sent through the queue; without a queue driver they will run as usual. +- The implementation of avatar upload changed, we're [no longer storing files temporarily on disk](https://github.com/flarum/core/pull/2117). +- The SES mail driver [has been removed](https://github.com/flarum/core/pull/2011). +- Mail driver backward compatibility from beta 12 has been removed, use the new Mail extender or implement the [modified interface](https://github.com/flarum/core/blob/master/src/Mail/DriverInterface.php). + +## Recommendations + +- Beta 14 will ship with a rewrite in the frontend (javascript). If you're building for that release, make sure to follow our [progress](https://github.com/flarum/core/pull/2126). + +## New Features + +- A ton of new extenders: + - [Middleware extender](https://github.com/flarum/core/pull/2017) + - [Console extender](https://github.com/flarum/core/pull/2057) + - [CSRF extender](https://github.com/flarum/core/pull/2095) + - [Event extender](https://github.com/flarum/core/pull/2097) + - [Mail extender](https://github.com/flarum/core/pull/2012) + - [Model extender](https://github.com/flarum/core/pull/2100) + +## Deprecations + +- Several events [have been marked deprecated](https://github.com/flarum/core/commit/4efdd2a4f2458c8703aae654f95c6958e3f7b60b) to be removed in beta 14. diff --git a/docs/tr/extend/update-b14.md b/docs/tr/extend/update-b14.md index e45ff2cf4..aea7a88e8 100644 --- a/docs/tr/extend/update-b14.md +++ b/docs/tr/extend/update-b14.md @@ -1,3 +1,777 @@ -# Beta 14 için Güncelleme +# Updating For Beta 14 -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +This release brings a large chunk of breaking changes - hopefully the last chunk of this size before our stable release. +In order to prepare the codebase for the upcoming stable release, we decided it was time to modernize / upgrade / exchange some of the underlying JavaScript libraries that are used in the frontend. +Due to the nature and size of these upgrades, we have to pass on some of the breaking changes to you, our extension developers. + +On the bright side, this overdue upgrade brings us closer to the conventions of best practices of [Mithril.js](https://mithril.js.org/), the mini-framework used for Flarum's UI. +Mithril's 2.0 release sports a more consistent component interface, which should be a solid foundation for years to come. +Where possible, we replicated old APIs, to ease the upgrade and give you time to do the full transition. +Quite a few breaking changes remain, though - read more below. + +::: tip + +If you need help with the upgrade, our friendly community will gladly help you out either [on the forum](https://discuss.flarum.org/t/extensibility) or [in chat](https://flarum.org/chat/). + +::: + +To ease the process, we've clearly separated the changes to the [frontend (JS)](#frontend-javascript) from those in the [backend (PHP)](#backend-php) below. +If your extension does not change the UI, consider yourself lucky. :-) + +A [summary of the frontend changes](#required-frontend-changes-recap) is available towards the end of this guide. + +## Frontend (JavaScript) + +### Mithril 2.0: Concepts + +Most breaking changes required by beta 14 are prompted by changes in Mithril 2. +[Mithril's upgrade guide](https://mithril.js.org/migration-v02x.html) is an extremely useful resource, and should be consulted for more detailed information. A few key changes are explained below: + +#### props -> attrs; initProps -> initAttrs + +Props passed into component are now referred to as `attrs`, and can be accessed via `this.attrs` where you would prior use `this.props`. This was done to be closer to Mithril's preferred terminology. We have provided a temporary backwards compatibility layer for `this.props`, but recommend using `this.attrs`. + +Accordingly, `initProps` has been replaced with `initAttrs`, with a similar BC layer. + +#### m.prop -> `flarum/utils/Stream` + +Mithril streams, which were available via `m.prop` in Mithril 0.2, are now available via `flarum/utils/Stream`. `m.prop` will still work for now due to a temporary BC layer. + +#### m.withAttr -> withAttr + +The `m.withAttr` util has been removed from Mithril. We have provided `flarum/utils/withAttr`, which does the same thing. A temporary BC layer has been added for `m.withAttr`. + +#### Lifecycle Hooks + +In mithril 0.2, we had 2 "lifecycle hooks": + +`init`, an unofficial hook which ran when the component instance was initialized. + +`config`, which ran when components were created, and on every redraw. + + +Mithril 2 has the following hooks; each of which take `vnode` as an argument: + +- `oninit` +- `oncreate` +- `onbeforeupdate` +- `onupdate` +- `onbeforeremove` +- `onremove` + +Please note that if your component is extending Flarum's helper `Component` class, you must call `super.METHOD(vnode)` if using `oninit`, `oncreate`, and `onbeforeupdate`. + +More information about what each of these do can be found [in Mithril's documentation](https://mithril.js.org/lifecycle-methods.html). + +A trivial example of how the old methods map to the new is: + +```js +class OldMithrilComponent extends Component { + init() { + console.log('Code to run when component instance created, but before attached to the DOM.'); + } + + config(element, isInitialized) { + console.log('Code to run on every redraw AND when the element is first attached'); + + if (isInitialized) return; + + console.log('Code to execute only once when components are first created and attached to the DOM'); + + context.onunload = () => { + console.log('Code to run when the component is removed from the DOM'); + } + } + + view() { + // In mithril 0, you could skip redrawing a component (or part of a component) by returning a subtree retain directive. + // See https://mithril.js.org/archive/v0.2.5/mithril.render.html#subtree-directives + // dontRedraw is a substitute for logic; usually, this is used together with SubtreeRetainer. + if (dontRedraw()) return { subtree: 'retain' }; + + return

Hello World!

; + } +} + +class NewMithrilComponent extends Component { + oninit(vnode) { + super.oninit(vnode); + + console.log('Code to run when component instance created, but before attached to the DOM.'); + } + + oncreate(vnode) { + super.oncreate(vnode); + + console.log('Code to run when components are first created and attached to the DOM'); + } + + onbeforeupdate(vnode, oldVnode) { + super.onbeforeupdate(vnode); + + console.log('Code to run BEFORE diffing / redrawing components on every redraw'); + + // In mithril 2, if we want to skip diffing / redrawing a component, we return "false" in its onbeforeupdate lifecycle hook. + // See https://mithril.js.org/lifecycle-methods.html#onbeforeupdate + // This is also typically used with SubtreeRetainer. + if (dontRedraw()) return false; + } + + onupdate(vnode) { + // Unlike config, this does NOT run when components are first attached. + // Some code might need to be replicated between oncreate and onupdate. + console.log('Code to run on every redraw AFTER the DOM is updated.'); + } + + onbeforeremove(vnode) { + // This is run before components are removed from the DOM. + // If a promise is returned, the DOM element will only be removed when the + // promise completes. It is only called on the top-level component that has + // been removed. It has no equivalent in Mithril 0.2. + // See https://mithril.js.org/lifecycle-methods.html#onbeforeremove + return Promise.resolve(); + } + + onremove(vnode) { + console.log('Code to run when the component is removed from the DOM'); + } +} +``` + +#### Children vs Text Nodes + +In Mithril 0.2, every child of a vnode is another vnode, stored in `vnode.children`. For Mithril 2, as a performance optimization, vnodes with a single text child now store that text directly in `vnode.text`. For developers, that means that `vnode.children` might not always contain the results needed. Luckily, text being stored in `vnode.text` vs `vnode.children` will be the same each time for a given component, but developers should be aware that at times, they might need to use `vnode.text` and not `vnode.children`. + +Please see [the mithril documentation](https://mithril.js.org/vnodes.html#structure) for more information on vnode structure. + +#### Routing API + +Mithril 2 introduces a few changes in the routing API. Most of these are quite simple: + +- `m.route()` to get the current route has been replaced by `m.route.get()` +- `m.route(NEW_ROUTE)` to set a new route has been replaced by `m.route.set(NEW_ROUTE)` +- When registering new routes, a component class should be provided, not a component instance. + +For example: + +```js +// Mithril 0.2 +app.routes.new_page = { path: '/new', component: NewPage.component() } + +// Mithril 2.0 +app.routes.new_page = { path: '/new', component: NewPage } +``` + +Additionally, the preferred way of defining an internal (doesn't refresh the page when clicked) link has been changed. The `Link` component should be used instead. + +```js +// Mithril 0.2 +Link Content + +// Mithril 2 +import Link from 'flarum/components/Link'; + +Link Content +``` + +You can also use `Link` to define external links, which will just render as plain `Children` html links. + +For a full list of routing-related changes, please see [the mithril documentation](https://mithril.js.org/migration-v02x.html). + +#### Redraw API + +Mithril 2 introduces a few changes in the redraw API. Most of these are quite simple: + +- Instead of `m.redraw(true)` for synchronous redraws, use `m.redraw.sync()` +- Instead of `m.lazyRedraw()`, use `m.redraw()` + +Remember that Mithril automatically triggers a redraw after DOM event handlers. The API for preventing a redraw has also changed: + +```js +// Mithril 0.2 + + +// Mithril 2 + +``` + +#### AJAX + +The `data` parameter of `m.request({...})` has been split up into `body` and `params`. + +For examples and other AJAX changes, see [the mithril documentation](https://mithril.js.org/migration-v02x.html#mrequest). + +#### Promises + +`m.deferred` has been removed, native promises should be used instead. For instance: + +```js +// Mithril 0.2 +const deferred = m.deferred(); + +app.store.find('posts').then(result => deferred.resolve(result)); + +return deferred.promise; + +// Mithril 2 +return app.store.find('posts'); +``` + +#### Component instances should not be stored + +Due to optimizations in Mithril's redrawing algorithms, [component instances should not be stored](https://mithril.js.org/components.html#define-components-statically,-call-them-dynamically). + +So whereas before, you might have done something like: + +```js +class ChildComponent extends Component { + oninit(vnode) { + super.oninit(vnode); + this.counter = 0; + } + + view() { + return

{this.counter}

; + } +} +class ParentComponent extends Component { + oninit(vnode) { + super.oninit(vnode); + this.child = new ChildComponent(); + } + + view() { + return ( +
+ + {this.child.render()} +
+ ) + } +} +``` + +That will no longer work. In fact; the Component class no longer has a render method. + +Instead, any data needed by a child component that is modified by a parent component should be passed in as an attr. For instance: + +```js +class ChildComponent extends Component { + view() { + return

{this.attrs.counter}

; + } +} + +class ParentComponent extends Component { + oninit(vnode) { + super.oninit(vnode); + this.counter = 0; + } + + view() { + return ( +
+ + +
+ ) + } +} +``` + +For more complex components, this might require some reorganization of code. For instance, let's say you have data that can be modified by several unrelated components. +In this case, it might be preferable to create a POJO "state instance' for this data. These states are similar to "service" singletons used in Angular and Ember. For instance: + +```js +class Counter { + constructor() { + this._counter = 0; + } + + increaseCounter() { + this._counter += 1; + } + + getCount() { + return this._counter; + } +} + +app.counter = new Counter(); + +extend(HeaderSecondary.prototype, 'items', function(items) { + items.add('counterDisplay', +
+

Counter: {app.counter.getCount()}

+
+ ); +}) + +extend(HeaderPrimary.prototype, 'items', function(items) { + items.add('counterButton', +
+ +
+ ); +}) +``` + +This "state pattern" can be found throughout core. Some non-trivial examples are: + +- PageState +- SearchState and GlobalSearchState +- NotificationListState +- DiscussionListState + +### Changes in Core + +#### Modals + +Previously, modals could be opened by providing a `Modal` component instance: + +```js +app.modal.show(new LoginModal(identification: 'prefilledUsername')); +``` + +Since we don't store component instances anymore, we pass in the component class and any attrs separately. + +```js +app.modal.show(LoginModal, {identification: 'prefilledUsername'}); +``` + +The `show` and `close` methods are still available through `app.modal`, but `app.modal` now points to an instance of `ModalManagerState`, not of the `ModalManager` component. +Any modifications by extensions should accordingly be done to `ModalManagerState`. + +#### Alerts + +Previously, alerts could be opened by providing an `Alert` component instance: + +```js +app.alerts.show(new Alert(type: 'success', children: 'Hello, this is a success alert!')); +``` + +Since we don't store component instances anymore, we pass in a component class, attrs, children separately. The `show` method has 3 overloads: + +```js +app.alerts.show('Hello, this is a success alert!'); +app.alerts.show({type: 'success'}, 'Hello, this is a success alert!'); +app.alerts.show(Alert, {type: 'success'}, 'Hello, this is a success alert!'); +``` + +Additionally, the `show` method now returns a unique key, which can then be passed into the `dismiss` method to dismiss that particular alert. +This replaces the old method of passing the alert instance itself to `dismiss`. + +The `show`, `dismiss`, and `clear` methods are still available through `app.alerts`, but `app.alerts` now points to an instance of `AlertManagerState`, not of the `AlertManager` component. +Any modifications by extensions should accordingly be done to `AlertManagerState`. + +#### Composer + +Since we don't store a component instances anymore, a number of util methods from `Composer`, `ComposerBody` (and it's subclasses), and `TextEditor` have been moved onto `ComposerState`. + +For `forum/components/Composer`, `isFullScreen`, `load`, `clear`, `show`, `hide`, `close`, `minimize`, `fullScreen`, and `exitFullScreen` have been moved to `forum/states/ComposerState`. They all remain accessible via `app.composer.METHOD` + +A `bodyMatches` method has been added to `forum/states/ComposerState`, letting you check whether a certain subclass of `ComposerBody` is currently open. + +Various input fields are now stored as [Mithril Streams](https://mithril.js.org/stream.html) in `app.composer.fields`. For instance, to get the current composer content, you could use `app.composer.fields.content()`. Previously, it was available on `app.composer.component.content()`. **This is a convention that `ComposerBody` subclasses that add inputs should follow.** + +`app.composer.component` is no longer available. + +- Instead of `app.composer.component instanceof X`, use `app.composer.bodyMatches(X)`. +- Instead of `app.composer.component.props`, use `app.composer.body.attrs`. +- Instead of `app.composer.component.editor`, use `app.composer.editor`. + +For `forum/components/TextEditor`, the `setValue`, `moveCursorTo`, `getSelectionRange`, `insertAtCursor`, `insertAt`, `insertBetween`, `replaceBeforeCursor`, `insertBetween` methods have been moved to `forum/components/SuperTextarea`. + +Also for `forum/components/TextEditor`, `this.disabled` is no longer used; `disabled` is passed in as an attr instead. It may be accessed externally via `app.composer.body.attrs.disabled`. + +Similarly to Modals and Alerts, `app.composer.load` no longer accepts a component instance. Instead, pass in the body class and any attrs. For instance, + +```js +// Mithril 0.2 +app.composer.load(new DiscussionComposer({user: app.session.user})); + +// Mithril 2 +app.composer.load(DiscussionComposer, {user: app.session.user}) +``` + +Finally, functionality for confirming before unloading a page with an active composer has been moved into the `common/components/ConfirmDocumentUnload` component. + +#### Widget and DashboardWidget + +The redundant `admin/components/Widget` component has been removed. `admin/components/DashboardWidget` should be used instead. + +#### NotificationList + +For `forum/components/NotificationList`, the `clear`, `load`, `loadMore`, `parseResults`, and `markAllAsRead` methods have been moved to `forum/states/NotificationListState`. + +Methods for `isLoading` and `hasMoreResults` have been added to `forum/states/NotificationListState`. + +`app.cache.notifications` is no longer available; `app.notifications` (which points to an instance of `NotificationListState`) should be used instead. + +#### Checkbox + +Loading state in the `common/components/Checkbox` component is no longer managed through `this.loading`; it is now passed in as a prop (`this.attrs.loading`). + +#### Preference Saver + +The `preferenceSaver` method of `forum/components/SettingsPage` has been removed without replacement. This is done to avoid saving component instances. Instead, preferences should be directly saved. For instance: + +```js +// Old way +Switch.component({ + children: app.translator.trans('core.forum.settings.privacy_disclose_online_label'), + state: this.user.preferences().discloseOnline, + onchange: (value, component) => { + this.user.pushAttributes({ lastSeenAt: null }); + this.preferenceSaver('discloseOnline')(value, component); + }, +}) + +// Without preferenceSaver +Switch.component({ + children: app.translator.trans('core.forum.settings.privacy_disclose_online_label'), + state: this.user.preferences().discloseOnline, + onchange: (value) => { + this.discloseOnlineLoading = true; + + this.user.savePreferences({ discloseOnline: value }).then(() => { + this.discloseOnlineLoading = false; + m.redraw(); + }); + }, + loading: this.discloseOnlineLoading, +}) +``` + +A replacement will eventually be introduced. + +#### DiscussionListState + +For `forum/components/DiscussionList`, the `requestParams`, `sortMap`, `refresh`, `loadResults`, `loadMore`, `parseResults`, `removeDiscussion`, and `addDiscussion` methods have been moved to `forum/states/DiscussionListState`. + +Methods for `hasDiscussions`, `isLoading`, `isSearchResults`, and `empty` have been added to `forum/states/DiscussionListState`. + +`app.cache.discussions` is no longer available; `app.discussions` (which points to an instance of `DiscussionListState`) should be used instead. + +#### PageState + +`app.current` and `app.previous` no longer represent component instances, they are now instances of the `common/states/PageState` class. This means that: + +- Instead of `app.current instanceof X`, use `app.current.matches(X)` +- Instead of `app.current.PROPERTY`, use `app.current.get('PROPERTY')`. Please note that all properties must be exposed EXPLICITLY via `app.current.set('PROPERTY', VALUE)`. + +#### PostStream + +Logic from `forum/components/PostStreamScrubber`'s `update` method has been moved to `forum/components/PostStream`'s `updateScrubber` method. + +For `forum/components/PostStream`, the `update`, `goToFirst`, `goToLast`, `goToNumber`, `goToIndex`, `loadNearNumber`, `loadNearIndex`, `loadNext`, `loadPrevious`, `loadPage`, `loadRange`, `show`, `posts`, `reset`, `count`, and `sanitizeIndex` methods have been moved to `forum/states/PostStreamState`. + +Methods for `disabled` and `viewingEnd` have been added to `forum/states/PostStreamState`. + +#### SearchState and GlobalSearchState + +As with other components, we no longer store instances of `forum/components/Search`. As such, every `Search` component instance should be paired with a `forum/states/SearchState` instance. + +At the minimum, `SearchState` contains the following methods: + +- getValue +- setValue +- clear +- cache (adds a searched value to cache, meaning that we don't need to search for its results again) +- isCached (checks if a value has been searched for before) + +All of these methods have been moved from `Search` to `SearchState`. Additionally, Search's `stickyParams`, `params`, `changeSort`, `getInitialSearch`, and `clearInitialSearch` methods have been moved to `forum/states/GlobalSearchState`, which is now available via `app.search`. + +To use a custom search, you'll want to: + +1. Possibly create a custom subclass of `SearchState` +2. Create a custom subclass of `Search`, which overrides the `selectResult` method to handle selecting results as needed by your use case, and modify the `sourceItems` methods to contain the search sources you need. + +#### moment -> dayjs + +The `moment` library has been removed, and replaced by the `dayjs` library. The global `moment` can still be used for now, but is deprecated. `moment` and `dayjs` have very similar APIs, so very few changes will be needed. Please see the dayjs documentation [for more information](https://day.js.org/en/) on how to use it. + +#### Subtree Retainer + +`SubtreeRetainer` is a util class that makes it easier to avoid unnecessary redraws by keeping track of some pieces of data. +When called, it checks if any of the data has changed; if not, it indicates that a redraw is not necessary. + +In mithril 0.2, its `retain` method returned a [subtree retain directive](https://mithril.js.org/archive/v0.1.25/mithril.render.html#subtree-directives) if no redraw was necessary. + +In mithril 2, we use its `needsRebuild` method in combination with `onbeforeupdate`. For instance: + +```js +class CustomComponent extends Component { + oninit(vnode) { + super.oninit(vnode); + + this.showContent = false; + + this.subtree = new SubtreeRetainer( + () => this.showContent, + ) + } + + onbeforeupdate() { + // If needsRebuild returns true, mithril will diff and redraw the vnode as usual. Otherwise, it will skip this redraw cycle. + // In this example, this means that this component and its children will only be redrawn when extra content is toggled. + return this.subtree.needsRebuild(); + } + + view(vnode) { + return
+ +

Hello World!{this.showContent ? ' Extra Content!' : ''}

+
; + } +} +``` + +#### attrs() method + +Previously, some components would have an attrs() method, which provided an extensible way to provide attrs to the top-level child vnode returned by `view()`. For instance, + +```js +class CustomComponent extends Component { + view() { + return

Hello World!

; + } + + attrs() { + return { + className: 'SomeClass', + onclick: () => console.log('click'), + }; + } +} +``` + +Since `this.attrs` is now used for attrs passed in from parent components, `attrs` methods have been renamed to `elementAttrs`. + +#### Children and .component + +Previously, an element could be created with child elements by passing those in as the `children` prop: + +```js +Button.component({ + className: 'Button Button--primary', + children: 'Button Text' +}); +``` + +This will no longer work, and will actually result in errors. Instead, the 2nd argument of the `component` method should be used: + +```js +Button.component({ + className: 'Button Button--primary' +}, 'Button Text'); +``` + +Children can still be passed in through JSX: + +```js + +``` + +#### Tag attr + +Because mithril uses 'tag' to indicate the actual html tag (or component class) used for a vnode, you can no longer pass `tag` as an attr to components +extending Flarum's `Component` helper class. The best workaround here is to just use another name for this attr. + +#### affixSidebar + +The `affixSidebar` util has been removed. Instead, if you want to affix a sidebar, wrap the sidebar code in an `AffixedSidebar` component. For instance, + +```js +class OldWay extends Component { + view() { + return
+
+
+ +
Actual Page Content
+
+
+
; + } +} + +class NewWay extends Component { + view() { + return
+
+
+ + + +
Actual Page Content
+
+
+
; + } +} +``` + +#### Fragment + +**Warning: For Advanced Use Only** + +In some rare cases, we want to have extremely fine grained control over the rendering and display of some significant chunks of the DOM. These are attached with `m.render`, and do not experience automated redraws. Current use cases in core and bundled extensions are: + +- The "Reply" button that shows up when selecting text in a post +- The mentions autocomplete menu that shows up when typing +- The emoji autocomplete menu that shows up when typing + +For this purpose, we provide a helper class (`common/Fragment`), of which you can create an instance, call methods, and render via `m.render(DOM_ROOT, fragmentInstance.render())`. The main benefit of using the helper class is that it allows you to use lifecycle methods, and to access the underlying DOM via `this.$()`, like you would be able to do with a component. + +This should only be used when absolutely necessary. If you are unsure, you probably don't need it. If the goal is to not store component instances, the "state pattern" as described above is preferable. + +### Required Frontend Changes Recap + +Each of these changes has been explained above, this is just a recap of major changes for your convenience. + +- Component Methods: + - `view()` -> `view(vnode)` + - Lifecycle + - `init()` -> `oninit(vnode)` + - `config()` -> Lifecycle hooks `oncreate(vnode)` / `onupdate(vnode)` + - `context.onunload()` -> `onremove()` + - `SubtreeRetainer` -> `onbeforeupdate()` + - if present, `attrs()` method needs to be renamed -> convention `elementAttrs()` + - building component with `MyComponent.component()` -> `children` is now second parameter instead of a named prop/attr (first argument) -> JSX preferred +- Routing + - `m.route()` -> `m.route.get()` + - `m.route(name)` -> `m.route.set(name)` + - register routes with page class, not instance + - special case when passing props + - `` -> `` +- AJAX + - `m.request({...})` -> `data:` key split up into `body:` and `params:` + - `m.deferred` -> native `Promise` +- Redrawing + - `m.redraw(true)` -> `m.redraw.sync()` + - `m.redraw.strategy('none')` -> `e.redraw = false` in event handler + - `m.lazyRedraw()` -> `m.redraw()` + +#### Deprecated changes + +For the following changes, we currently provide a backwards-compatibility layer. +This will be removed in time for the stable release. +The idea is to let you release a new version that's compatible with Beta 14 to your users as quickly as possible. +When you have taken care of the changes above, you should be good to go. +For the following changes, we have bought you time until the stable release. +Considering you have to make changes anyway, why not do them now? + +- `this.props` -> `this.attrs` +- static `initProps()` -> static `initAttrs()` +- `m.prop` -> `flarum/utils/Stream` +- `m.withAttr` -> `flarum/utils/withAttr` +- `moment` -> `dayjs` + +## Backend (PHP) + +### New Features + +#### Extension Dependencies + +Some extensions are based on, or add features to, other extensions. +Prior to this release, there was no way to ensure that those dependencies were enabled before the extension that builds on them. +Now, you cannot enable an extension unless all of its dependencies are enabled, and you cannot disable an extension if there are other enabled extensions depending on it. + +So, how do we specify dependencies for an extension? Well, all you need to do is add them as composer dependencies to your extension's `composer.json`! For instance, if we have an extension that depends on Tags and Mentions, our `composer.json` will look like this: + +```json +{ + "name": "my/extension", + "description": "Cool New Extension", + "type": "flarum-extension", + "license": "MIT", + "require": { + "flarum/core": "^0.1.0-beta.14", + "flarum/tags": "^0.1.0-beta.14", // Will mark tags as a dependency + "flarum/mentions": "^0.1.0-beta.14", // Will mark mentions as a dependency + } + // other config +} +``` + +#### View Extender + +Previously, when extensions needed to register Laravel Blade views, they could inject a view factory in `extend.php` and call it's `addNamespace` method. For instance, + +```php +// extend.php +use Illuminate\Contracts\View\Factory; + +return [ + function (Factory $view) { + $view->addNamespace(NAME, RELATIVE PATH); + } +] +``` + +This should NOT be used, as it will break views for all extensions that boot after yours. Instead, the `View` extender should be used: + +```php +// extend.php +use Flarum\Extend\View; + +return [ + (new View)->namespace(NAME, RELATIVE PATH); +] +``` + +#### Application and Container + +Although Flarum uses multiple components of the Laravel framework, it is not a pure Laravel system. In beta 14, the `Flarum\Foundation\Application` class no longer implements `Illuminate\Contracts\Foundation\Application`, and no longer inherits `Illuminate\Container\Container`. Several things to note: + +- The `app` helper now points to an instance of `Illuminate\Container\Container`, not `Flarum\Foundation\Application`. You might need to resolve things through the container before using them: for instance, `app()->url()` will no longer work; you'll need to resolve or inject an instance of `Flarum\Foundation\Config` and use that. +- Injected or resolved instances of `Flarum\Foundation\Application` can no longer resolve things through container methods. `Illuminate\Container\Container` should be used instead. +- Not all public members of `Illuminate\Contracts\Foundation\Application` are available through `Flarum\Foundation\Application`. Please refer to our [API docs on `Flarum\Foundation\Application`](https://api.docs.flarum.org/php/master/flarum/foundation/application) for more information. + +#### Other Changes + +- We are now using Laravel 6. Please see [Laravel's upgrade guide](https://laravel.com/docs/6.x/upgrade) for more information. Please note that we do not use all of Laravel. +- Optional params in url generator now work. For instance, the url generator can now properly generate links to posts in discussions. +- A User Extender has been added, which replaces the deprecated `PrepareUserGroups` and `GetDisplayName` events. +- Error handler middleware can now be manipulated by the middleware extender through the `add`, `remove`, `replace`, etc methods, just like any other middleware. +- `Flarum/Foundation/Config` and `Flarum/Foundation/Paths` can now be injected where needed; previously their data was accessible through `Flarum/Foundation/Application`. + +### Deprecations + +- `url` provided in `config.php` is now an array, accessible via `$config->url()`, for an instance of `Config` - [PR](https://github.com/flarum/core/pull/2271#discussion_r475930358) +- AssertPermissionTrait has been deprecated - [Issue](https://github.com/flarum/core/issues/1320) +- Global path helpers and path methods of `Application` have been deprecated, the injectable `Paths` class should be used instead - [PR](https://github.com/flarum/core/pull/2155) +- `Flarum\User\Event\GetDisplayName` has been deprecated, the `displayNameDriver` method of the `User` extender should be used instead - [PR](https://github.com/flarum/core/pull/2174) + +### Removals + +- Do NOT use the old closure notation for configuring view namespaces. This will break all extensions that boot after your extension. The `View` extender MUST be used instead. +- app()->url() will no longer work: [`Flarum\Http\UrlGenerator`](routes.md) should be injected and used instead. An instance of `Flarum\Http\UrlGenerator` is available in `blade.php` templates via `$url`. +- As a part of the Laravel 6 upgrade, the [`array_` and `str_` helpers](https://laravel.com/docs/6.x/upgrade#helpers) have been removed. +- The Laravel translator interface has been removed; the Symfony translator interface should be used instead: `Symfony\Component\Translation\TranslatorInterface` +- The Mandrill mail driver is no longer provided in Laravel 6, and has been removed. +- The following events deprecated in Beta 13 [have been removed](https://github.com/flarum/core/commit/7d1ef9d89161363d1c8dea19cf8aebb30136e9e3#diff-238957b67e42d4e977398cd048c51c73): + - `AbstractConfigureRoutes` + - `ConfigureApiRoutes` - Use the `Routes` extender instead + - `ConfigureForumRoutes` - Use the `Frontend` or `Routes` extenders instead + - `ConfigureLocales` - Use the `LanguagePack` extender instead + - `ConfigureModelDates` - Use the `Model` extender instead + - `ConfigureModelDefaultAttributes` - Use the `Model` extender instead + - `GetModelRelationship` - Use the `Model` extender instead + - `Console\Event\Configuring` - Use the `Console` extender instead + - `BioChanged` - User bio has not been a core feature for several releases diff --git a/docs/tr/extend/update-b15.md b/docs/tr/extend/update-b15.md index 5d6b51ebd..6606a9e57 100644 --- a/docs/tr/extend/update-b15.md +++ b/docs/tr/extend/update-b15.md @@ -1,3 +1,60 @@ -# Beta 15 için Güncelleme +# Updating For Beta 15 -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +Beta 15 features multiple new extenders, a total redesign of the admin dashboard, and several other interesting new features for extensions. As before, we have done our best to provide backwards compatibility layers, and we recommend switching away from deprecated systems as soon as possible to make your extensions more stable. + +::: tip + +If you need help applying these changes or using new features, please start a discussion on the [community forum](https://discuss.flarum.org/t/extensibility) or [Discord chat](https://flarum.org/discord/). + +::: + +## New Features / Deprecations + +### Extenders + +- `Flarum\Api\Event\WillGetData` and `Flarum\Api\Event\WillSerializeData` have been deprecated, the `ApiController` extender should be used instead +- `Flarum\Api\Event\Serializing` and `Flarum\Event\GetApiRelationship` have been deprecated, the `ApiSerializer` extender should be used instead +- `Flarum\Formatter\Event\Parsing` has been deprecated, the `parse` method of the `Formatter` extender should be used instead +- `Flarum\Formatter\Event\Rendering` has been deprecated, the `render` method of the `Formatter` extender should be used instead +- `Flarum\Notification\Event\Sending` has been deprecated, the `driver` method of the `Notification` extender should be used instead + - Please note that the new notification driver system is not an exact analogue of the old `Sending` event, as it can only add new drivers, not change the functionality of the default notification bell alert driver. If your extension needs to modify **how** or **to whom** notifications are sent, you may need to replace `Flarum\Notification\NotificationSyncer` on the service provider level +- `Flarum\Event\ConfigureNotificationTypes` has been deprecated, the `type` method of the `Notification` extender should be used instead +- `Flarum\Event\ConfigurePostTypes` has been deprecated, the `type` method of the `Post` extender should be used instead +- `Flarum\Post\Event\CheckingForFlooding` has been deprecated, as well as `Flarum\Post\Floodgate`. They have been replaced with a middleware-based throttling system that applies to ALL requests to /api/*, and can be configured via the `ThrottleApi` extender. Please see our [api-throttling](api-throttling.md) documentation for more information. +- `Flarum\Event\ConfigureUserPreferences` has been deprecated, the `registerPreference` method of the `User` extender should be used instead +- `Flarum\Foundation\Event\Validating` has been deprecated, the `configure` method of the `Validator` extender should be used instead + +- The Policy system has been reworked a bit to be more intuitive. Previously, policies contained both actual policies, which determine whether a user can perform some ability, and model visibility scopers, which allowed efficient restriction of queries to only items that users have access to. See the [authorization documentation](authorization.md) for more information on how to use the new systems. Now: + - `Flarum\Event\ScopeModelVisibility` has been deprecated. New scopers can be registered via the `ModelVisibility` extender, and any `Eloquent\Builder` query can be scoped by calling the `whereVisibleTo` method on it, with the ability in question as an optional 2nd argument (defaults to `view`). + - `Flarum\Event\GetPermission` has been deprecated. Policies can be registered via the `Policy` extender. `Flarum\User\User::can` has not changed. Please note that the new policies must return one of `$this->allow()`, `$this->deny()`, `$this->forceAllow()`, `$this->forceDeny()`, not a boolean. + +- A `ModelUrl` extender has been added, allowing new slug drivers to be registered. This accompanies Flarum's new slug driving system, which allows for extensions to define custom slugging strategies for sluggable models. The extender supports sluggable models outside of Flarum core. Please see our [model slugging](slugging.md) documentation for more information. +- A `Settings` extender has been added, whose `serializeToForum` method makes it easy to serialize a setting to the forum. +- A `ServiceProvider` extender has been added. This should be used with extreme caution for advanced use cases only, where there is no alternative. Please note that the service provider layer is not considered public API, and is liable to change at any time, without notice. + +### Admin UX Redesign + +The admin dashboard has been completely redesigned, with a focus on providing navbar pages for each extension. The API for extensions to register settings, permissions, and custom pages has also been greatly simplified. You can also now update your extension's `composer.json` to provide links for funding, support, website, etc that will show up on your extension's admin page. Please see [our Admin JS documentation](./admin.md) for more information on how to use the new system. + +### Other New Features + +- On the backend, the route name is now available via `$request->getAttribute('routeName')` for controllers, and for middleware that run after `Flarum\Http\Middleware\ResolveRoute.php`. +- `Flarum\Api\Controller\UploadImageController.php` can now be used as a base class for controllers that upload images (like for the logo and favicon). +- Automatic browser scroll restoration can now be disabled for individual pages [see our frontend page documentation for more info](frontend-pages.md). + +## Breaking Changes + +- The following deprecated frontend BC layers were removed: + - `momentjs` no longer works as an alias for `dayjs` + - `this.props` and `this.initProps` no longer alias `this.attrs` and `this.initAttrs` for the `Component` base class + - `m.withAttr` and `m.stream` no longer alias `flarum/utils/withAttr` and `flarum/utils/Stream` + - `app.cache.discussionList` has been removed + - `this.content` and `this.editor` have been removed from `ComposerBody` + - `this.component`, `this.content`, and `this.value` have been removed from `ComposerState` +- The following deprecated backend BC layers were removed: + - The `publicPath`, `storagePath`, and `vendorPath` methods of `Flarum\Foundation\Application` have been removed + - The `base_path`, `public_path`, and `storage_path` global helpers have been removed + - The `getEmailSubject` method of `Flarum\Notification\MailableInterface` MUST now take a translator instance as an argument + - `Flarum\User\AssertPermissionTrait` has been removed, the analogous methods on `Flarum\User\User` should be used instead + - The `Flarum\Event\PrepareUserGroups` event has been removed, use the `User` extender instead + - The `Flarum\User\Event\GetDisplayName` event has been removed, use the display name driver feature of the `User` extender instead diff --git a/docs/tr/extend/update-b16.md b/docs/tr/extend/update-b16.md new file mode 100644 index 000000000..8dcbf2095 --- /dev/null +++ b/docs/tr/extend/update-b16.md @@ -0,0 +1,141 @@ +# Updating For Beta 16 + +Beta 16 finalizes the PHP extender API, introduces a testing library and JS typings, switches to using namespaces for JS imports, increases extension dependency robustness, and allows overriding routes, among other features. + +::: tip + +If you need help applying these changes or using new features, please start a discussion on the [community forum](https://discuss.flarum.org/t/extensibility) or [Discord chat](https://flarum.org/discord/). + +::: + +## Frontend + +- A new editor driver abstraction has been introduced, which allows extensions to override the default textarea-based editor with more advanced solutions. +- The `TextEditor` and `TextEditorButton` components, as well as the `BasicEditorDriver` util (which replaces `SuperTextarea`) have been moved from `forum` to `common`. +- The `forum`, `admin`, and `common` namespaces should be used when importing. So instead of `import Component from 'flarum/Component'`, use `import Component from 'flarum/common/Component`. Support for the old import styles will be deprecated through the stable release, and removed with Flarum 2.0. +- A typing library has been released to support editor autocomplete for frontend development, and can be installed in your dev environment via `npm install --save-dev flarum@0.1.0-beta.16`. +- Extension categories have been simplified down to `feature`, `theme`, and `language`. + +## Backend + +### Extenders + +- All extenders that support callbacks/closures now support global functions like `'boolval'` and array-type functions like `[ClassName::class, 'methodName']`. +- The `Settings` extender's `serializeToFrontend` method now supports a default value as the 4th argument. +- The `Event` extender now supports registering subscribers for multiple events at once via a `subscribe` method. +- The `Notification` extender now has a `beforeSending` method, which allows you to adjust the list of recipients before a notification is sent. +- The `mutate` method of `ApiSerializer` has been deprecated, and renamed to `attributes`. +- `remove` methods on the `Route` and `Frontend` extenders can be used to remove (and then replace) routes. +- A `ModelPrivate` extender replaces the `GetModelIsPrivate` event, which has been deprecated. +- Methods on the `Auth` extender replace the `CheckingPassword` event, which has been deprecated. +- All search-related events are now deprecated in favor of the `SimpleFlarumSearch` and `Filter` extenders; this is explained in more detail below. + +### Laravel and Symfony + +Beta 16 upgrades from v6.x to v8.x of Laravel components and v4 to v5 of Symfony components. Please see the respective upgrade guides of each for changes you might need to make to your extensions. +The most applicable change is the deprecation of `Symfony\Component\Translation\TranslatorInterface` in favor of `Symfony\Contracts\Translation\TranslatorInterface`. The former will be removed in beta 17. + +### Helper Functions + +The remaining `app` and `event` global helper functions have been deprecated. `app` has been replaced with `resolve`, which takes the name of a container binding and resolves it through the container. + +Since some Flarum extensions use Laravel libraries that assume some global helpers exist, we've recreated some commonly used helpers in the [flarum/laravel-helpers](https://github.com/flarum/laravel-helpers) package. These helpers should NOT be used directly in Flarum extension code; they are available so that Laravel-based libraries that expect them to exist don't malfunction. + +### Search Changes + +As part of our ongoing efforts to make Flarum's search system more flexible, we've made several refactors in beta 16. +Most notably, filtering and searching are now treated as different mechanisms, and have separate pipelines and extenders. +Essentially, if a query has a `filter[q]` query param, it will be treated as a search, and all other filter params will be ignored. Otherwise, it will be handled by the filtering system. This will eventually allow searches to be handled by alternative drivers (provided by extensions), such as ElasticSearch, without impacting filtering (e.g. loading recent discussions). Classes common to both systems have been moved to a `Query` namespace. + +Core's filtering and default search (named SimpleFlarumSearch) implementations are quite similar, as both are powered by the database. `List` API controllers call the `search` / `filter` methods on a resource-specific subclass of `Flarum\Search\AbstractSearcher` or `Flarum\Filter\AbstractFilterer`. Arguments are an instance of `Flarum\Query\QueryCriteria`, as well as sort, offset, and limit information. Both systems return an instance of `Flarum\Query\QueryResults`, which is effectively a wrapper around a collection of Eloquent models. + +The default systems are also somewhat similar in their implementation. `Filterer`s apply Filters (implementing `Flarum\Filter\FilterInterface`) based on query params in the form `filter[FILTER_KEY] = FILTER_VALUE` (or `filter[-FILTER_KEY] = FILTER_VALUE` for negated filters). SimpleFlarumSearch's `Searcher`s split the `filter[q]` param by spaces into "terms", apply Gambits (implementing `Flarum\Search\GambitInterface`) that match the terms, and then apply a "Fulltext Gambit" to search based on any "terms" that don't match an auxiliary gambit. Both systems then apply sorting, an offset, and a result count limit, and allow extensions to modify the query result via `searchMutators` or `filterMutators`. + +Extensions add gambits and search mutators and set fulltext gambits for `Searcher` classes via the `SimpleFlarumSearch` extender. They can add filters and filter mutators to `Filterer` classes via the `Filter` extender. + +With regards to upgrading, please note the following: + +- Search mutations registered by listening to the `Searching` events for discussions and users will be applied as to searches during the search mutation step via a temporary BC layer. They WILL NOT be applied to filters. This is a breaking change. These events have been deprecated. +- Search gambits registered by listening to the `ConfigureUserGambits` and `ConfigureDiscussionGambits` events will be applied to searcher via a temporary BC layer. They WILL NOT be applied to filters. This is a breaking change. These events have been deprecated. +- Post filters registered by listening to the `ConfigurePostsQuery` events will be applied to post filters via a temporary BC layer. That event has been deprecated. + +### Testing Library + +The `flarum/testing` package provides utils for PHPUnit-powered automated backend tests. See the [testing documentation](testing.md) for more info. + +### Optional Dependencies + +Beta 15 introduced "extension dependencies", which require any extensions listed in your extension's `composer.json`'s `require` section to be enabled before your extension can be enabled. + +With beta 16, you can specify "optional dependencies" by listing their composer package names as an array in your extension's `extra.flarum-extension.optional-dependencies`. Any enabled optional dependencies will be booted before your extension, but aren't required for your extension to be enabled. + +### Access Token and Authentication Changes + +#### Extension API changes + +The signature to various method related to authentication have been changed to take `$token` as parameter instead of `$userId`. Other changes are the result of the move from `$lifetime` to `$type` + +- `Flarum\Http\AccessToken::generate($userId)` no longer accepts `$lifetime` as a second parameter. Parameter has been kept for backward compatibility but has no effect. It will be removed in beta 17. +- `Flarum\Http\RememberAccessToken::generate($userId)` should be used to create remember access tokens. +- `Flarum\Http\DeveloperAccessToken::generate($userId)` should be used to create developer access tokens (don't expire). +- `Flarum\Http\SessionAccessToken::generate()` can be used as an alias to `Flarum\Http\AccessToken::generate()`. We might deprecate `AccessToken::generate()` in the future. +- `Flarum\Http\Rememberer::remember(ResponseInterface $response, AccessToken $token)`: passing an `AccessToken` has been deprecated. Pass an instance of `RememberAccessToken` instead. As a temporary compatibility layer, passing any other type of token will convert it into a remember token. In beta 17 the method signature will change to accept only `RememberAccessToken`. +- `Flarum\Http\Rememberer::rememberUser()` has been deprecated. Instead you should create/retrieve a token manually with `RememberAccessToken::generate()` and pass it to `Rememberer::remember()` +- `Flarum\Http\SessionAuthenticator::logIn(Session $session, $userId)` second parameter has been deprecated and is replaced with `$token`. Backward compatibility is kept. In beta 17, the second parameter method signature will change to `AccessToken $token`. +- `AccessToken::generate()` now saves the model to the database before returning it. +- `AccessToken::find($id)` or `::findOrFail($id)` can no longer be used to find a token, because the primary key was changed from `token` to `id`. Instead you can use `AccessToken::findValid($tokenString)` +- It's recommended to use `AccessToken::findValid($tokenString): AccessToken` or `AccessToken::whereValid(): Illuminate\Database\Eloquent\Builder` to find a token. This will automatically scope the request to only return valid tokens. On forums with low activity this increases the security since the automatic deletion of outdated tokens only happens every 50 requests on average. + +#### Symfony session changes + +If you are directly accessing or manipulating the Symfony session object, the following changes have been made: + +- `user_id` attribute is no longer used. `access_token` has been added as a replacement. It's a string that maps to the `token` column of the `access_tokens` database table. + +To retrieve the current user from inside a Flarum extension, the ideal solution which was already present in Flarum is to use `$request->getAttribute('actor')` which returns a `User` instance (which might be `Guest`) + +To retrieve the token instance from Flarum, you can use `Flarum\Http\AccessToken::findValid($tokenString)` + +To retrieve the user data from a non-Flarum application, you'll need to make an additional database request to retrieve the token. The user ID is present as `user_id` on the `access_tokens` table. + +#### Token creation changes + +The `lifetime` property of access tokens has been removed. Tokens are now either `session` tokens with 1h lifetime after last activity, or `session_remember` tokens with 5 years lifetime after last activity. + +The `remember` parameter that was previously available on the `POST /login` endpoint has been made available on `POST /api/token`. It doesn't return the remember cookie itself, but the token returned can be used as a remember cookie. + +The `lifetime` parameter of `POST /api/token` has been deprecated and will be removed in Flarum beta 17. Partial backward compatibility has been provided where a `lifetime` value longer than 3600 seconds is interpreted like `remember=1`. Values lower than 3600 seconds result in a normal non-remember token. + +New `developer` tokens that don't expire have been introduced, however they cannot be currently created through the REST API. Developers can create developer tokens from an extension using `Flarum\Http\DeveloperAccessToken::generate($userId)`. + +If you manually created tokens in the database from outside Flarum, the `type` column is now required and must contain `session`, `session_remember` or `developer`. Tokens of unrecognized type cannot be used to authenticate, but won't be deleted by the garbage collector either. In a future version extensions will be able to register custom access token types. + +#### Token usage changes + +A [security issue in Flarum](https://github.com/flarum/core/issues/2075) previously caused all tokens to never expire. This had limited security impact due to tokens being long unique characters. However custom integrations that saved a token in an external database for later use might find the tokens no longer working if they were not used recently. + +If you use short-lived access tokens for any purpose, take note of the expiration time of 1h. The expiration is based on the time of last usage, so it will remain valid as long as it continues to be used. + +Due to the large amount of expired tokens accumulated in the database and the fact most tokens weren't ever used more than once during the login process, we have made the choice to delete all access tokens a lifetime of 3600 seconds as part of the migration, All remaining tokens have been converted to `session_remember` tokens. + +#### Remember cookie + +The remember cookie still works like before, but a few changes have been made that could break unusual implementations. + +Now only access tokens created with `remember` option can be used as remember cookie. Any other type of token will be ignored. This means if you create a token with `POST /api/token` and then place it in the cookie manually, make sure you set `remember=1` when creating the token. + +#### Web session expiration + +In previous versions of Flarum, a session could be kept alive forever until the Symfony session files were deleted from disk. + +Now sessions are linked to access tokens. A token being deleted or expiring will automatically end the linked web session. + +A token linked to a web session will now be automatically deleted from the database when the user clicks logout. This prevents any stolen token from being re-used, but it could break custom integration that previously used a single access token in both a web session and something else. + +### Miscellaneous + +- The IP address is now available in requests via `$request->getAttribute('ipAddress')` +- Policies can now return `true` and `false` as aliases for `$this->allow()` and `$this->deny()`, respectively. +- The `user.edit` permission has been split into `user.editGroups`, `user.editCredentials` (for email, username, and password), and `user.edit` (for other attributes). +- There are now permissions (`bypassTagCounts`) that allow users to bypass tag count requirements. +- Flarum now supports PHP 7.3 - PHP 8.0, with support for PHP 7.2 officially dropped. diff --git a/docs/tr/extend/update-b8.md b/docs/tr/extend/update-b8.md index 6b0ad9d0d..27ae779cb 100644 --- a/docs/tr/extend/update-b8.md +++ b/docs/tr/extend/update-b8.md @@ -1,3 +1,114 @@ -# Beta 8 için Güncelleme +# Updating For Beta 8 -**Yakında Türkçe belgeler eklenecektir.** \ No newline at end of file +All extensions will need to be refactored in order to work with beta 8. Here are the main things you will need to do in order to make your extension compatible. + +::: warning + +This guide is not comprehensive. You may encounter some changes we haven't documented. If you need help, start a discussion on the [community forum](https://discuss.flarum.org/t/extensibility) or [Discord chat](https://flarum.org/discord/). + +::: + +## PHP Namespaces + +Beta 8 comes with large changes to the overall structure of the PHP backend. You will need to look through [this list](https://discuss.flarum.org/d/6572-help-us-namespace-changes) of namespace changes and make changes to your extension accordingly. + +[This script](https://gist.github.com/tobyzerner/55e7c05c95404e5efab3a9e43799d375) can help you to automate most of the namespace changes. Of course, you should still test your extension after running the script as it may miss something. + +## Database Naming + +Many database columns and JSON:API attributes have been renamed to conform to a [convention](/contributing.md#database). You will need to update any instances where your extension interacts with core data. You can see the changes in [#1344](https://github.com/flarum/core/pull/1344/files). + +## Extenders + +Beta 8 introduces a new concept called **extenders** that replace the most common event listeners. You can learn more about how they work in the [updated extension docs](start.md#extenders). + +`bootstrap.php` has been renamed to `extend.php` and returns an array of extender instances and functions: + +```php +use Flarum\Extend; + +return [ + (new Extend\Frontend('forum')) + ->js(__DIR__.'/js/dist/forum.js') + ->css(__DIR__.'/less/forum.less') + ->route('/t/{slug}', 'tag') + ->route('/tags', 'tags'), + + function (Dispatcher $events) { + $events->subscribe(Listener\AddForumTagsRelationship::class); + } +] +``` + +If you're listening for any of the following events, you'll need to update your code to use an extender instead. See the relevant docs for more information. + +| Event | Extender | +| ----------------------------------- | ------------------------- | +| `Flarum\Event\ConfigureFormatter`* | `Flarum\Extend\Formatter` | +| `Flarum\Event\ConfigureWebApp`* | `Flarum\Extend\Frontend` | +| `Flarum\Event\ConfigureClientView`* | `Flarum\Extend\Frontend` | +| `Flarum\Event\ConfigureLocales` | `Flarum\Extend\Locales` | +| `Flarum\Event\ConfigureApiRoutes` | `Flarum\Extend\Routes` | +| `Flarum\Event\ConfigureForumRoutes` | `Flarum\Extend\Routes` | + +_\* class no longer exists_ + +## JavaScript Tooling + +Previously Flarum and its extensions used a custom Gulp workflow to compile ES6 source code into something that browsers could understand. Beta 8 switches to a more conventional approach with Webpack. + +You will need to tweak the structure of your extension's `js` directory. Currently, your JS file hierarchy looks something like the following: + +``` +js +├── admin +│ ├── src +│ │ └── main.js +│ ├── dist +│ │ └── extension.js +│ ├── Gulpfile.js +│ └── package.json +└── forum + ├── src + │ └── main.js + ├── dist + │ └── extension.js + ├── Gulpfile.js + └── package.json +``` + +You'll need to make the following changes: + +1. Update `package.json` and create `webpack.config.js`, `forum.js`, and `admin.js` files using [these templates](frontend.html#transpilation). + +2. Inside your `admin` and `forum` *folders*, delete `Gulpfile.js`, `package.json`, and `dist`. Then inside each `src` folder, rename `main.js` to `index.js`. Now move all of the `src` files outside of `src` folder and delete it. + +3. In the root `js` folder create a folder called `src` and move your `admin` and `forum` *folders* into it. + +4. While still in your root `js` folder, run `npm install` and then `npm run build` to build the new JS dist files. + +If everything went right, your folder structure should look something like this: + +``` +js +├── src +│ ├── admin +│ │ └── index.js +│ └── forum +│ └── index.js +├── dist +│ ├── admin.js +│ ├── admin.js.map +│ ├── forum.js +│ └── forum.js.map +├── admin.js +├── forum.js +├── package.json +└── webpack.config.js +``` + +Take a look at the [bundled extensions](https://github.com/flarum) for more examples. + +## Font Awesome Icons + +Beta 8 upgrades to Font Awesome 5, in which icon class names have changed. The `flarum/helpers/icon` helper now requires the **full Font Awesome icon class names** to be passed, eg. `fas fa-bolt`. diff --git a/docs/tr/extenders.md b/docs/tr/extenders.md new file mode 100644 index 000000000..4aa80847b --- /dev/null +++ b/docs/tr/extenders.md @@ -0,0 +1,25 @@ +# Local Extenders + +Sitenizde bir uzantının tamamını dağıtmadan yapmak istediğiniz özelleştirmeler varsa, bunu **yerel genişleticileri** kullanarak yapabilirsiniz. Her Flarum kurulumu, tıpkı tam bir uzantıda olduğu gibi, genişletici örnekleri ekleyebileceğiniz bir `extend.php` dosyasıylabirlikte gelir. + +Genişleticiler (ve hatta bir [yerel genişletici örneği](extend/start.md#hello-world)) hakkında daha fazla bilgi için [uzantı belgelerimize](extend/start.md) bakın. + +Yeni dosyalar oluşturmanız gerekiyorsa (genişleticiler için içe aktarılacak özel bir sınıf eklerken), composer.json dosyanızı biraz ayarlamanız gerekir. +Aşağıdakileri ekleyin: + +```json +"autoload": { + "psr-4": { + "App\\": "app/" + } +}, +``` + +Artık bir `app` alt dizininde `App\...` ad alanını kullanarak yeni PHP dosyaları oluşturabilirsiniz. + +::: tip Yerel Genişleticiler ve Uzantılar + +Yerel genişleticiler küçük ayarlamalar için iyi olabilir, ancak büyük özelleştirmelere ihtiyacınız varsa, bir uzantı daha iyi bir seçim olabilir: +ayrı bir kod tabanı, birçok dosyanın daha temiz işlenmesi, geliştirici araçları ve kaynağı kolayca açma yeteneği büyük avantajlardır. + +::: \ No newline at end of file diff --git a/docs/tr/extensions.md b/docs/tr/extensions.md index f25ac1313..4590dba22 100644 --- a/docs/tr/extensions.md +++ b/docs/tr/extensions.md @@ -17,6 +17,18 @@ Flarum gibi, uzantılar da SSH kullanılarak [Composer](https://getcomposer.org) 1. `composer.json` dosyasını içeren klasöre `cd` komutuyla gidin. 2. Besteciyi çalıştırmak için `composer require COMPOSER_PACKAGE_NAME` gereklidir. Bu, uzantının belgesi tarafından sağlanmalıdır. +## Updating Extensions + +Follow the instructions provided by extension developers. If you're using `*` as the version string for extensions ([as is recommended](composer.md)), running the commands listed in the [Flarum upgrade guide](update.md) should update all your extensions. + +## Uninstalling Extensions + +Similarly to installation, to remove an extension: + +0. If you want to remove all database tables created by the extension, click the "Uninstall" button in the admin dashboard. See [below](#managing-extensions) for more information. +1. `cd` to your Flarum directory. +2. Run `composer remove COMPOSER_PACKAGE_NAME:*`. This should be provided by the extension's documentation. + ## Uzantıları Yönetme Yönetici panosunun uzantılar sayfası, yüklendiklerinde uzantıları yönetmek için uygun bir yol sağlar. Yapabilecekleriniz: diff --git a/docs/tr/install.md b/docs/tr/install.md index 36443d83a..007c74b69 100644 --- a/docs/tr/install.md +++ b/docs/tr/install.md @@ -1,13 +1,5 @@ # Kurulum -::: danger Uyarı - -Flarum **beta yazılımıdır**. Bu, hala bazı eksik özelliklere ve hatalara sahip olduğu anlamına gelir 🐛🐞 ve bir noktada - er ya da geç - muhtemelen kırılacaktır! 💥 - -Beta, tamamen bu sorunları çözmek ve Flarum'u iyileştirmekle ilgilidir. **Ne yaptığınızı bilmiyorsanız lütfen üretimde Flarum kullanmayın**. İşler ters giderse sizi destekleyemeyiz. Sonraki sürümlere yükseltmek mümkün olacak, ancak ellerinizi kirletmeyi içerebilir. - -::: - ::: tip Hızlı test? Flarum'u [gösteri forumlarımızdan](https://discuss.flarum.org/d/21101) birinde denemekten çekinmeyin. Veya Flarum ekibine bağlı olmayan ücretsiz bir topluluk hizmeti olan [Free Flarum](https://www.freeflarum.com)'da kendi forumunuzu birkaç saniye içinde kurun. @@ -36,10 +28,10 @@ Bu mutlaka bir VPS'ye ihtiyacınız olduğu anlamına gelmez. Bazı paylaşılan Flarum, bağımlılıklarını ve uzantılarını yönetmek için [Composer](https://getcomposer.org) kullanır. Flarum'u kurmadan önce, makinenize [Composer'ı kurmanız](https://getcomposer.org) gerekir. Daha sonra, bu komutu Flarum'un yüklenmesini istediğiniz boş bir konumda çalıştırın: ```bash -composer create-project flarum/flarum . --stability=beta +composer create-project flarum/flarum . ``` -Bu komut çalışırken web sunucunuzu yapılandırabilirsiniz. Root klasörünüzü `/path/to/your/forum/public` olarak ayarlandığından emin olmanız ve aşağıdaki talimatlara göre [URL Yeniden Yazma] (# url-yeniden yazma) ayarlamanız gerekir. +Bu komut çalışırken web sunucunuzu yapılandırabilirsiniz. Root klasörünüzü `/path/to/your/forum/public` olarak ayarlandığından emin olmanız ve aşağıdaki talimatlara göre [URL Yönlendirme](#url-yonlendirme) ayarlamanız gerekir. Her şey hazır olduğunda, bir web tarayıcısında forumunuza gidin ve kurulumu tamamlamak için talimatları izleyin. diff --git a/docs/tr/troubleshoot.md b/docs/tr/troubleshoot.md index 299815227..a438b043d 100644 --- a/docs/tr/troubleshoot.md +++ b/docs/tr/troubleshoot.md @@ -4,13 +4,13 @@ Flarum beklendiği gibi yüklenmiyorsa veya çalışmıyorsa, yapmanız gereken Ayrıca, [Destek forumunu](https://discuss.flarum.org/t/support) ve [sorun izleyiciyi](https://github.com/flarum/core/issues) aramak için birkaç dakikanızı ayırmalısınız. Birisi sorunu zaten bildirmiş olabilir ve bir düzeltme mevcut veya yolda. İyice araştırdıysanız ve sorunla ilgili herhangi bir bilgi bulamıyorsanız, sorun gidermeye başlamanın zamanı geldi. -## Adım 0: Hata ayıklama modunu açın +## 1. Adım: Hata ayıklama modunu açın Devam etmeden önce, Flarum'un hata ayıklama araçlarını etkinleştirmelisiniz. Basitçe bir metin düzenleyiciyle **config.php** açın, `debug` değerini `true` olarak değiştirin ve dosyayı kaydedin. Bu, Flarum'un ayrıntılı hata mesajları göstermesine neden olarak size neyin yanlış gittiğine dair bir fikir verecektir. Boş sayfalar görüyorsanız ve yukarıdaki değişiklik yardımcı olmuyorsa, **php.ini** yapılandırma dosyanızda `display_errors` ı `On` olarak ayarlamayı deneyin. -## 1. Adım: Yaygın düzeltmeler +## 2. Adım: Yaygın düzeltmeler Aşağıdakilerle birçok sorun çözülebilir: @@ -22,7 +22,7 @@ Aşağıdakilerle birçok sorun çözülebilir: Ayrıca önemli hiçbir şeyin yerinde olmadığından emin olmak için [`php flarum info`](console.md) çıktısına da göz atmak isteyeceksiniz. -## 2. Adım: Sorunu yeniden oluşturun +## 3. Adım: Sorunu yeniden oluşturun Sorunun yeniden oluşmasını sağlamaya çalışın. Gerçekleştiğinde ne yaptığınıza dikkat edin. Her seferinde mi yoksa sadece ara sıra mı oluyor? Sorunu etkileyebileceğini düşündüğünüz bir ayarı veya bir şeyleri yaptığınız sırayı değiştirmeyi deneyin. Bazı koşullarda olurken diğerleri olmuyor mu? @@ -30,7 +30,7 @@ Yakın zamanda bir uzantı eklediyseniz veya güncellediyseniz, sorunun çözül Yol boyunca bir yerlerde sorununuza neyin sebep olduğu hakkında bir fikir edinebilir ve bunu düzeltmenin bir yolunu bulabilirsiniz. Ancak bu olmasa bile, hata raporunuzu doldurduktan sonra, neler olup bittiğini anlamamıza yardımcı olacak birkaç değerli ipucuyla karşılaşacaksınız. -## 3. Adım: Bilgi toplayın +## 4. Adım: Bilgi toplayın Sorunu çözmek için yardıma ihtiyacınız olacak gibi görünüyorsa, veri toplama konusunda ciddi olmanın zamanı geldi. Aşağıdaki yerlerde hata mesajlarını veya sorunla ilgili diğer bilgileri arayın: @@ -42,7 +42,7 @@ Sorunu çözmek için yardıma ihtiyacınız olacak gibi görünüyorsa, veri to Herhangi bir mesajı bir metin dosyasına kopyalayın ve hatanın *ne zaman* oluştuğu, o sırada *ne yaptığınız* vb. Hakkında birkaç not alın. Sorunun meydana geldiği ve oluşmadığı koşullar hakkında derlemiş olabileceğiniz tüm bilgileri eklediğinizden emin olun. Sunucu ortamınız hakkında olabildiğince fazla bilgi ekleyin: İşletim sistemi sürümü, web sunucusu sürümü, PHP sürümü ve işleyici, vb. -## 4. Adım: Bir rapor hazırlayın +## 5. Adım: Bir rapor hazırlayın Sorunla ilgili tüm bilgileri topladıktan sonra bir hata raporu vermeye hazırsınız. Lütfen [Hataları Bildirme](bugs.md) ile ilgili talimatları uygulayın. diff --git a/docs/tr/update.md b/docs/tr/update.md index 48a37dca3..d13de99b6 100644 --- a/docs/tr/update.md +++ b/docs/tr/update.md @@ -1,3 +1,100 @@ -# Güncelleme +# Updating -Flarum beta sürümündeyken, nasıl güncelleme yapılacağına ilişkin talimatlar her [yayın duyurusunda](https://discuss.flarum.org/t/blog?sort=newest) yayınlanacaktır. \ No newline at end of file +To update Flarum, you'll need to use [Composer](https://getcomposer.org). If you're not familiar with it (although you should be, because you need it to install Flarum), read [our guide](composer.md) for information on what it is and how to set it up. + +If updating across major versions (e.g. <=0.1.0 to 1.x.x, 1.x.x to 2.x.x, ...), make sure to read the appropiate "major version update guide" before running the general upgrade steps. + +## General Steps + +**Step 1:** Make sure all your extensions have versions compatible with the Flarum version you're trying to install. This is only needed across major versions (e.g. you probably don't need to check this if upgrading from v1.0.0 to v1.1.0, assuming your extensions follow recommended versioning). You can check this by looking at the extension's [Discuss thread](https://discuss.flarum.org/t/extensions), searching for it on [Packagist](http://packagist.org/), or checking databases like [Extiverse](https://extiverse.com). You'll need to remove (not just disable) any uncompatible extensions before updating. Please be patient with extension developers! + +**Step 2:** Take a look at your `composer.json` file. Unless you have a reason to require specific versions of extensions or libraries, you should set the version string of everything except `flarum/core` to `*` (including `flarum/tags`, `flarum/mentions`, and other bundled extensions). Make sure `flarum/core` is NOT set to `*`. If you're targeting a specific version of Flarum, set `flarum/core` to that (e.g. `"flarum/core": "v0.1.0-beta.16`). If you just want the most recent version, use `"flarum/core": "^1.0"`. + +**Step 4:** If your local install uses [local extenders](extenders.md), make sure they are up to date with changes in Flarum. + +**Step 4:** We recommend disabling third-party extensions in the admin dashboard before updating. This isn't strictly required, but will make debugging easier if you run into issues. + +**Step 5:** Make sure your PHP version is supported by the version of Flarum you are trying to upgrade to, and that you are using Composer 2 (`composer --version)`. + +**Step 6:** Finally, to update, run: + +``` +composer update --prefer-dist --no-plugins --no-dev -a --with-all-dependencies +php flarum migrate +php flarum cache:clear +``` + +**Step 7:** If applicable, restart your PHP process and opcache. + +## Major Version Update Guides + +### Updating from Beta (<=0.1.0) to Stable v1 (^1.0.0) + +1. Do steps 1-5 above. +2. Change the version strings of all bundled extensions (`flarum/tags`, `flarum/mentions`, `flarum/likes`, etc) in `composer.json` from `^0.1.0` to `*`. +3. Change `flarum/core`'s version string in `composer.json` from `^0.1.0` to `^1.0`. +4. Remove the `"minimum-stability": "beta",` line from your `composer.json` +5. Do steps 6 and 7 above. + +## Troubleshooting Issues + +There are 2 main places where you might run into errors when updating Flarum: while running the update command itself, or when accessing the forum after updating. + +### Errors While Updating + +Here we'll go through several common types of issues while trying to update Flarum. + +--- + +If the output is short and contains: + +``` +Nothing to modify in lock file +``` + +Or does not list `flarum/core as an updated package, and you are not on the latest flarum version: + +- Revisit step 2 above, make sure that all third party extensions have an asterisk for their version string. +- Make sure your `flarum/core` version requirement isn't locked to a specific minor version (e.g. `v0.1.0-beta.16` is locked, `^1.0.0` isn't). If you're trying to update across major versions of Flarum, follow the related major version update guide above. + +--- + +For other errors, try running `composer why-not flarum/core VERSION_YOU_WANT_TO_UPGRADE_TO` + +If the output looks something like this: + +``` +flarum/flarum - requires flarum/core (v0.1.0-beta.15) +fof/moderator-notes 0.4.4 requires flarum/core (>=0.1.0-beta.15 <0.1.0-beta.16) +jordanjay29/flarum-ext-summaries 0.3.2 requires flarum/core (>=0.1.0-beta.14 <0.1.0-beta.16) +flarum/core v0.1.0-beta.16 requires dflydev/fig-cookies (^3.0.0) +flarum/flarum - does not require dflydev/fig-cookies (but v2.0.3 is installed) +flarum/core v0.1.0-beta.16 requires franzl/whoops-middleware (^2.0.0) +flarum/flarum - does not require franzl/whoops-middleware (but 0.4.1 is installed) +flarum/core v0.1.0-beta.16 requires illuminate/bus (^8.0) +flarum/flarum - does not require illuminate/bus (but v6.20.19 is installed) +flarum/core v0.1.0-beta.16 requires illuminate/cache (^8.0) +flarum/flarum - does not require illuminate/cache (but v6.20.19 is installed) +flarum/core v0.1.0-beta.16 requires illuminate/config (^8.0) +flarum/flarum - does not require illuminate/config (but v6.20.19 is installed) +flarum/core v0.1.0-beta.16 requires illuminate/container (^8.0) +flarum/flarum - does not require illuminate/container (but v6.20.19 is installed) +flarum/core v0.1.0-beta.16 requires illuminate/contracts (^8.0) +flarum/flarum - does not require illuminate/contracts (but v6.20.19 is installed) +flarum/core v0.1.0-beta.16 requires illuminate/database (^8.0) +flarum/flarum - does not require illuminate/database (but v6.20.19 is installed) +flarum/core v0.1.0-beta.16 requires illuminate/events (^8.0) +flarum/flarum - does not require illuminate/events (but v6.20.19 is installed) +... (this'll go on for a bit) +``` + +It is very likely that some of your extensions have not yet been updated. + +- Revisit step 1 again, make sure all your extensions have versions compatible with the core version you want to upgrade to. Remove any that don't. +- Make sure you're running `composer update` with all the flags specified in the update step. + +If none of this fixes your issue, feel free to reach out on our [Support forum](https://discuss.flarum.org/t/support). Make sure to include the output of `php flarum info` and `composer why-not flarum/core VERSION_YOU_WANT_TO_UPGRADE_TO`. + +### Errors After Updating + +If you are unable to access your forum after updating, follow our [troubleshooting instructions](troubleshoot.md). From 4bb7c4d6e458029cad70bb59a03a6bf4b506d148 Mon Sep 17 00:00:00 2001 From: DursunCanPoyraz Date: Thu, 27 May 2021 21:17:23 +0300 Subject: [PATCH 2/2] tr-1.0.0-dev-0.2 Sidebar buttons. --- docs/.vuepress/config/locales/tr/sidebar.js | 66 ++++++++++++++------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/docs/.vuepress/config/locales/tr/sidebar.js b/docs/.vuepress/config/locales/tr/sidebar.js index 5dc4e1545..9376eb7dc 100644 --- a/docs/.vuepress/config/locales/tr/sidebar.js +++ b/docs/.vuepress/config/locales/tr/sidebar.js @@ -1,5 +1,5 @@ module.exports = { - '/tr/extend/': [ + '/extend/': [ { title: 'Ana Kavramlar', collapsable: false, @@ -8,14 +8,9 @@ module.exports = { 'start', 'frontend', 'routes', - 'data', - 'distribution', - 'update-b8', - 'update-b10', - 'update-b12', - 'update-b13', - 'update-b14', - 'update-b15', + 'models', + 'api', + 'distribution' ] }, { @@ -23,10 +18,17 @@ module.exports = { collapsable: false, children: [ 'admin', + 'backend-events', + 'authorization', 'frontend-pages', 'interactive-components', + 'i18n', + 'language-packs', 'forms', - 'backend-events', + 'permissions', + 'settings', + 'testing', + 'theme' ] }, { @@ -34,20 +36,34 @@ module.exports = { collapsable: false, children: [ 'api-throttling', - 'authorization', + 'assets', 'console', + 'extending-extensions', + 'extensibility', + 'filesystem', 'formatting', - 'i18n', 'mail', 'middleware', + 'model-visibility', 'slugging', 'notifications', - 'permissions', 'post-types', 'search', - 'service-provider', - 'settings', - 'testing', + 'service-provider' + ] + }, + { + title: 'Güncelleme Kılavuzları', + collapsable: false, + children: [ + 'update-1.0', + 'update-b16', + 'update-b15', + 'update-b14', + 'update-b13', + 'update-b12', + 'update-b10', + 'update-b8', ] }, // { @@ -65,7 +81,7 @@ module.exports = { // } ], - '/tr/': [ + '/': [ { title: 'Giriş', collapsable: false, @@ -79,26 +95,34 @@ module.exports = { ] }, { - title: 'Ayarlama', + title: 'Kurulum', collapsable: false, children: [ + 'composer', 'install', 'update', 'troubleshoot' ] }, { - title: 'Yönetim', + title: 'Yönetims', collapsable: false, children: [ 'admin', 'config', - 'extensions', + 'extensisons', 'languages', 'themes', 'mail', 'console' ] + }, + { + title: 'İleri', + collapsable: false, + children: [ + 'extenders' + ] } ], -} \ No newline at end of file +}