This commit is contained in:
SychO9 2021-09-01 17:20:06 +01:00
commit 5d3804c7ca
36 changed files with 5496 additions and 0 deletions

View File

@ -0,0 +1,19 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 2
[*.{diff,md}]
trim_trailing_whitespace = false
[*.{php,xml,json}]
indent_size = 4

View File

@ -0,0 +1,91 @@
name: JS
on: [workflow_dispatch, push, pull_request]
env:
NODE_VERSION: 16
jobs:
prettier:
name: Prettier
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
cache-dependency-path: js/package-lock.json
- name: Install JS dependencies
run: npm ci
working-directory: ./js
- name: Check JS formatting
run: npm run format-check
working-directory: ./js
build-prod:
name: Build and commit
runs-on: ubuntu-latest
needs: [prettier]
# Only commit JS on push to master branch
# Remember to change in `build-test` job too
if: github.ref == 'refs/heads/<%= mainGitBranch %>' && github.event_name == 'push'
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
cache-dependency-path: js/package-lock.json
# Our action will install npm, cd into `./js`, run `npm run build` and
# `npm run build-typings`, then commit and upload any changes
- name: Build production JS
uses: flarum/action-build@2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
build_script: build
package_manager: yarn
# typings_script: build-typings
build-test:
name: Test build
runs-on: ubuntu-latest
needs: [prettier]
# Inverse check of `build-prod`
# Remember to change in `build-prod` job too
if: github.ref != 'refs/heads/<%= mainGitBranch %>' || github.event_name != 'push'
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
cache-dependency-path: js/package-lock.json
# Our action will install npm, cd into `./js`, run `npm run build` and
# `npm run build-typings`, then commit and upload any changes
- name: Build production JS
uses: flarum/action-build@2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
build_script: build
package_manager: yarn
# typings_script: build-typings
do_not_commit: true

View File

@ -0,0 +1,78 @@
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: [7.3, 7.4, '8.0']
service: ['mysql:5.7', mariadb]
prefix: ['', flarum_]
include:
- service: 'mysql:5.7'
db: MySQL
- service: mariadb
db: MariaDB
- prefix: flarum_
prefixStr: (prefix)
exclude:
- php: 7.3
service: 'mysql:5.7'
prefix: flarum_
- php: 7.3
service: mariadb
prefix: flarum_
- php: 8.0
service: 'mysql:5.7'
prefix: flarum_
- php: 8.0
service: mariadb
prefix: flarum_
services:
mysql:
image: ${{ matrix.service }}
ports:
- 13306:3306
name: 'PHP ${{ matrix.php }} / ${{ matrix.db }} ${{ matrix.prefixStr }}'
steps:
- uses: actions/checkout@master
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: xdebug
extensions: curl, dom, gd, json, mbstring, openssl, pdo_mysql, tokenizer, zip
tools: phpunit, composer:v2
# The authentication alter is necessary because newer mysql versions use the `caching_sha2_password` driver,
# which isn't supported prior to PHP7.4
# When we drop support for PHP7.3, we should remove this from the setup.
- name: Create MySQL Database
run: |
sudo systemctl start mysql
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" --port 13306
- name: Install Composer dependencies
run: composer install
- name: Setup Composer tests
run: composer test:setup
env:
DB_PORT: 13306
DB_PASSWORD: root
DB_PREFIX: ${{ matrix.prefix }}
- name: Run Composer tests
run: composer test
env:
COMPOSER_PROCESS_TIMEOUT: 600

3
extensions/package-manager/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
js/node_modules
vendor/
composer.lock

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Sami Mazouz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,3 @@
# Package Manager
*An Experiment.*

View File

@ -0,0 +1,39 @@
{
"name": "sycho/flarum-package-manager",
"description": "A Flarum Package Manager.",
"keywords": [
"extensions", "composer", "packages", "manager", "updater"
],
"type": "flarum-extension",
"license": "MIT",
"authors": [
{
"name": "Sami Mazouz",
"email": "sami.mazouz@flarum.org",
"homepage": "https://sycho9.github.io"
}
],
"support": {
"issues": "https://github.com/SychO9/flarum-package-manager/issues",
"source": "https://github.com/SychO9/flarum-package-manager"
},
"require": {
"flarum/core": "^1.0.0",
"composer/composer": "^2.0"
},
"extra": {
"flarum-extension": {
"title": "Package Manager",
"icon": {
"name": "fas fa-box-open",
"backgroundColor": "#117187",
"color": "#fff"
}
}
},
"autoload": {
"psr-4": {
"SychO\\PackageManager\\": "src/"
}
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
*
*/
namespace SychO\PackageManager;
use Flarum\Extend;
use Flarum\Foundation\Paths;
use Illuminate\Console\Scheduling\Event;
use SychO\PackageManager\Console\TaskCommand;
return [
(new Extend\Routes('api'))
->post('/package-manager/extensions', 'package-manager.extensions.require', Api\Controller\RequireExtensionController::class)
->patch('/package-manager/extensions/{id}', 'package-manager.extensions.update', Api\Controller\UpdateExtensionController::class)
->delete('/package-manager/extensions/{id}', 'package-manager.extensions.remove', Api\Controller\RemoveExtensionController::class),
(new Extend\Frontend('admin'))
->css(__DIR__ . '/less/admin.less')
->js(__DIR__ . '/js/dist/admin.js'),
new Extend\Locales(__DIR__ . '/locale'),
(new Extend\ServiceProvider)
->register(ComposerEnvironmentProvider::class),
(new Extend\Console)
->schedule(TaskCommand::class, function (Event $event) {
$event
->everyMinute()
->withoutOverlapping()
->appendOutputTo(resolve(Paths::class)->storage.'/logs/bazaar-tasks.log');
}),
];

View File

@ -0,0 +1 @@
export * from './src/admin';

View File

@ -0,0 +1,373 @@
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./admin.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "../../core/js/node_modules/@babel/runtime/helpers/esm/inheritsLoose.js":
/*!***********************************************************************************************************!*\
!*** /home/samilyas/www/flarum/packages/core/js/node_modules/@babel/runtime/helpers/esm/inheritsLoose.js ***!
\***********************************************************************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return _inheritsLoose; });
/* harmony import */ var _setPrototypeOf_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./setPrototypeOf.js */ "../../core/js/node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js");
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
Object(_setPrototypeOf_js__WEBPACK_IMPORTED_MODULE_0__["default"])(subClass, superClass);
}
/***/ }),
/***/ "../../core/js/node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js":
/*!************************************************************************************************************!*\
!*** /home/samilyas/www/flarum/packages/core/js/node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js ***!
\************************************************************************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return _setPrototypeOf; });
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
/***/ }),
/***/ "./admin.js":
/*!******************!*\
!*** ./admin.js ***!
\******************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _src_admin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/admin */ "./src/admin/index.js");
/* empty/unused harmony star reexport */
/***/ }),
/***/ "./src/admin/components/Installer.tsx":
/*!********************************************!*\
!*** ./src/admin/components/Installer.tsx ***!
\********************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return Installer; });
/* harmony import */ var _babel_runtime_helpers_esm_inheritsLoose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/esm/inheritsLoose */ "../../core/js/node_modules/@babel/runtime/helpers/esm/inheritsLoose.js");
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! flarum/admin/app */ "flarum/admin/app");
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(flarum_admin_app__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var flarum_common_Component__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! flarum/common/Component */ "flarum/common/Component");
/* harmony import */ var flarum_common_Component__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(flarum_common_Component__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! flarum/common/components/Button */ "flarum/common/components/Button");
/* harmony import */ var flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var flarum_common_utils_Stream__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! flarum/common/utils/Stream */ "flarum/common/utils/Stream");
/* harmony import */ var flarum_common_utils_Stream__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(flarum_common_utils_Stream__WEBPACK_IMPORTED_MODULE_4__);
var Installer = /*#__PURE__*/function (_Component) {
Object(_babel_runtime_helpers_esm_inheritsLoose__WEBPACK_IMPORTED_MODULE_0__["default"])(Installer, _Component);
function Installer() {
var _this;
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _Component.call.apply(_Component, [this].concat(args)) || this;
_this.packageName = void 0;
_this.isLoading = false;
return _this;
}
var _proto = Installer.prototype;
_proto.oninit = function oninit(vnode) {
_Component.prototype.oninit.call(this, vnode);
this.packageName = flarum_common_utils_Stream__WEBPACK_IMPORTED_MODULE_4___default()('');
};
_proto.view = function view() {
return m("div", {
className: "Form-group"
}, m("label", {
htmlFor: "install-extension"
}, flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.translator.trans('sycho-package-manager.admin.extensions.install')), m("p", {
className: "helpText"
}, flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.translator.trans('sycho-package-manager.admin.extensions.install_help', {
extiverse: m("a", {
href: "https://extiverse.com"
}, "extiverse.com")
})), m("div", {
className: "FormControl-container"
}, m("input", {
className: "FormControl",
id: "install-extension",
placeholder: "vendor/package-name",
bidi: this.packageName
}), m(flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3___default.a, {
className: "Button",
icon: "fas fa-download",
onclick: this.onsubmit.bind(this),
loading: this.isLoading
}, flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.translator.trans('sycho-package-manager.admin.extensions.proceed'))));
};
_proto.data = function data() {
return {
"package": this.packageName()
};
};
_proto.onsubmit = function onsubmit() {
var _this2 = this;
this.isLoading = true;
flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.request({
method: 'POST',
url: flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.forum.attribute('apiUrl') + "/package-manager/extensions",
body: {
data: this.data()
}
}).then(function () {
_this2.isLoading = false;
flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.alerts.show({
type: 'success',
message: flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.translator.trans('core.lib.success')
});
m.redraw();
})["catch"](function () {
_this2.isLoading = false;
m.redraw();
});
};
return Installer;
}(flarum_common_Component__WEBPACK_IMPORTED_MODULE_2___default.a);
/***/ }),
/***/ "./src/admin/index.js":
/*!****************************!*\
!*** ./src/admin/index.js ***!
\****************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var flarum_common_extend__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! flarum/common/extend */ "flarum/common/extend");
/* harmony import */ var flarum_common_extend__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(flarum_common_extend__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! flarum/admin/app */ "flarum/admin/app");
/* harmony import */ var flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(flarum_admin_app__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var flarum_admin_components_ExtensionPage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! flarum/admin/components/ExtensionPage */ "flarum/admin/components/ExtensionPage");
/* harmony import */ var flarum_admin_components_ExtensionPage__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(flarum_admin_components_ExtensionPage__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! flarum/common/components/Button */ "flarum/common/components/Button");
/* harmony import */ var flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _components_Installer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./components/Installer */ "./src/admin/components/Installer.tsx");
flarum_admin_app__WEBPACK_IMPORTED_MODULE_1___default.a.initializers.add('sycho-package-manager', function (app) {
app.extensionData["for"]('sycho-package-manager').registerSetting(function () {
return m(_components_Installer__WEBPACK_IMPORTED_MODULE_4__["default"], null);
});
Object(flarum_common_extend__WEBPACK_IMPORTED_MODULE_0__["extend"])(flarum_admin_components_ExtensionPage__WEBPACK_IMPORTED_MODULE_2___default.a.prototype, 'topItems', function (items) {
var _this = this;
items.add('remove', m(flarum_common_components_Button__WEBPACK_IMPORTED_MODULE_3___default.a, {
className: "Button Button--danger",
icon: "fas fa-times",
onclick: function onclick() {
app.request({
url: app.forum.attribute('apiUrl') + "/package-manager/extensions/" + _this.extension.id,
method: 'DELETE'
}).then(function () {
app.alerts.show({
type: 'success',
message: 'Success!'
});
});
}
}, "Remove"));
});
});
/***/ }),
/***/ "flarum/admin/app":
/*!**************************************************!*\
!*** external "flarum.core.compat['admin/app']" ***!
\**************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = flarum.core.compat['admin/app'];
/***/ }),
/***/ "flarum/admin/components/ExtensionPage":
/*!***********************************************************************!*\
!*** external "flarum.core.compat['admin/components/ExtensionPage']" ***!
\***********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = flarum.core.compat['admin/components/ExtensionPage'];
/***/ }),
/***/ "flarum/common/Component":
/*!*********************************************************!*\
!*** external "flarum.core.compat['common/Component']" ***!
\*********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = flarum.core.compat['common/Component'];
/***/ }),
/***/ "flarum/common/components/Button":
/*!*****************************************************************!*\
!*** external "flarum.core.compat['common/components/Button']" ***!
\*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = flarum.core.compat['common/components/Button'];
/***/ }),
/***/ "flarum/common/extend":
/*!******************************************************!*\
!*** external "flarum.core.compat['common/extend']" ***!
\******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = flarum.core.compat['common/extend'];
/***/ }),
/***/ "flarum/common/utils/Stream":
/*!************************************************************!*\
!*** external "flarum.core.compat['common/utils/Stream']" ***!
\************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = flarum.core.compat['common/utils/Stream'];
/***/ })
/******/ });
//# sourceMappingURL=admin.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
{
"name": "@sycho/flarum-package-manager",
"version": "0.0.0",
"private": true,
"prettier": "@flarum/prettier-config",
"dependencies": {
"flarum-webpack-config": "^1.0.0",
"flarum-tsconfig": "^1.0.0",
"@flarum/prettier-config": "^1.0.0",
"webpack": "^4.26.0",
"webpack-cli": "^3.0.7"
},
"devDependencies": {
"prettier": "^2.3.0"
},
"scripts": {
"dev": "webpack --mode development --watch",
"build": "webpack --mode production",
"format": "prettier --write src",
"format-check": "prettier --check src"
}
}

View File

@ -0,0 +1,58 @@
import type Mithril from 'mithril';
import app from 'flarum/admin/app';
import Component from 'flarum/common/Component';
import Button from "flarum/common/components/Button";
import Stream from "flarum/common/utils/Stream";
export default class Installer extends Component {
packageName!: Stream<string>;
isLoading: boolean = false;
oninit(vnode: Mithril.Vnode): void {
super.oninit(vnode);
this.packageName = Stream('');
}
view(): Mithril.Children {
return (
<div className="Form-group">
<label htmlFor="install-extension">{app.translator.trans('sycho-package-manager.admin.extensions.install')}</label>
<p className="helpText">{app.translator.trans('sycho-package-manager.admin.extensions.install_help', {
extiverse: <a href="https://extiverse.com">extiverse.com</a>
})}</p>
<div className="FormControl-container">
<input className="FormControl" id="install-extension" placeholder="vendor/package-name" bidi={this.packageName}/>
<Button className="Button" icon="fas fa-download" onclick={this.onsubmit.bind(this)} loading={this.isLoading}>
{app.translator.trans('sycho-package-manager.admin.extensions.proceed')}
</Button>
</div>
</div>
);
}
data(): any {
return {
package: this.packageName(),
};
}
onsubmit(): void {
this.isLoading = true;
app.request({
method: 'POST',
url: `${app.forum.attribute('apiUrl')}/package-manager/extensions`,
body: {
data: this.data()
},
}).then(() => {
this.isLoading = false;
app.alerts.show({ type: 'success', message: app.translator.trans('core.lib.success')});
m.redraw();
}).catch(() => {
this.isLoading = false;
m.redraw();
});
}
}

View File

@ -0,0 +1,34 @@
import { extend } from 'flarum/common/extend';
import app from 'flarum/admin/app';
import ExtensionPage from 'flarum/admin/components/ExtensionPage';
import Button from 'flarum/common/components/Button';
import Installer from "./components/Installer";
app.initializers.add('sycho-package-manager', (app) => {
app.extensionData
.for('sycho-package-manager')
.registerSetting(() => {
return (
<Installer />
);
});
extend(ExtensionPage.prototype, 'topItems', function (items) {
items.add(
'remove',
<Button
className="Button Button--danger"
icon="fas fa-times"
onclick={() => {
app.request({
url: `${app.forum.attribute('apiUrl')}/package-manager/extensions/${this.extension.id}`,
method: 'DELETE',
}).then(() => {
app.alerts.show({ type: 'success', message: 'Success!' });
});
}}>
Remove
</Button>
);
});
});

View File

@ -0,0 +1,11 @@
{
"extends": "flarum-tsconfig",
"include": ["src/**/*"],
"compilerOptions": {
"declarationDir": "./dist-typings",
"baseUrl": ".",
"paths": {
"flarum/*": ["../vendor/flarum/core/js/dist-typings/*"]
}
}
}

View File

@ -0,0 +1 @@
module.exports = require('flarum-webpack-config')();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
.FormControl-container {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
}

View File

@ -0,0 +1,7 @@
sycho-package-manager:
admin:
extensions:
install: Install a new extension
install_help: Fill in the extension package name to proceed. Visit {extiverse} to browse extensions.
proceed: Proceed

View File

@ -0,0 +1,19 @@
<?php
use Flarum\Database\Migration;
use Illuminate\Database\Schema\Blueprint;
return Migration::createTable(
'generic_tasks',
function (Blueprint $table) {
$table->increments('id');
$table->string('status', 50)->nullable();
$table->string('command', 50);
$table->string('command_class')->nullable();
$table->string('package', 100)->nullable();
$table->mediumText('output');
$table->dateTime('created_at');
$table->dateTime('started_at')->nullable();
$table->dateTime('finished_at')->nullable();
}
);

View File

@ -0,0 +1,25 @@
<?php
namespace SychO\PackageManager\Api\Controller;
use Flarum\Http\RequestUtil;
use SychO\PackageManager\Api\Serializer\TaskSerializer;
use SychO\PackageManager\Task;
use Flarum\Api\Controller\AbstractListController;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class ListTaskController extends AbstractListController
{
public $serializer = TaskSerializer::class;
/**
* @throws \Flarum\User\Exception\PermissionDeniedException
*/
protected function data(ServerRequestInterface $request, Document $document)
{
RequestUtil::getActor($request)->assertAdmin();
return Task::query()->orderBy('created_at', 'desc')->get();
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace SychO\PackageManager\Api\Controller;
use Flarum\Api\Controller\AbstractDeleteController;
use Flarum\Bus\Dispatcher;
use Flarum\Http\RequestUtil;
use Illuminate\Support\Arr;
use SychO\PackageManager\Api\Serializer\ExtensionSerializer;
use Psr\Http\Message\ServerRequestInterface;
use SychO\PackageManager\Command\RemoveExtension;
class RemoveExtensionController extends AbstractDeleteController
{
public $serializer = ExtensionSerializer::class;
/**
* @var Dispatcher
*/
protected $bus;
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* @throws \Flarum\User\Exception\PermissionDeniedException
*/
protected function delete(ServerRequestInterface $request)
{
$actor = RequestUtil::getActor($request);
$extensionId = Arr::get($request->getQueryParams(), 'id');
$this->bus->dispatch(
new RemoveExtension($actor, $extensionId)
);
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace SychO\PackageManager\Api\Controller;
use Flarum\Bus\Dispatcher;
use SychO\PackageManager\Api\Serializer\ExtensionSerializer;
use SychO\PackageManager\Command\RequireExtension;
use SychO\PackageManager\Extension\ExtensionUtils;
use Flarum\Api\Controller\AbstractCreateController;
use Flarum\Http\RequestUtil;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use Tobscure\JsonApi\Document;
class RequireExtensionController extends AbstractCreateController
{
public $serializer = ExtensionSerializer::class;
/**
* @var Dispatcher
*/
protected $bus;
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = RequestUtil::getActor($request);
$package = Arr::get($request->getParsedBody(), 'data.package');
$this->bus->dispatch(
new RequireExtension($actor, $package)
);
return null;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace SychO\PackageManager\Api\Controller;
use Flarum\Bus\Dispatcher;
use Flarum\Http\RequestUtil;
use SychO\PackageManager\Api\Serializer\ExtensionSerializer;
use Flarum\Api\Controller\AbstractShowController;
use Illuminate\Support\Arr;
use Psr\Http\Message\ServerRequestInterface;
use SychO\PackageManager\Command\UpdateExtension;
use Tobscure\JsonApi\Document;
class UpdateExtensionController extends AbstractShowController
{
public $serializer = ExtensionSerializer::class;
/**
* @var Dispatcher
*/
protected $bus;
public function __construct(Dispatcher $bus)
{
$this->bus = $bus;
}
/**
* @throws \Flarum\User\Exception\PermissionDeniedException
*/
protected function data(ServerRequestInterface $request, Document $document)
{
$actor = RequestUtil::getActor($request);
$extensionId = Arr::get($request->getQueryParams(), 'id');
$this->bus->dispatch(
new UpdateExtension($actor, $extensionId)
);
return null;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace SychO\PackageManager\Command;
use Flarum\User\User;
class RemoveExtension
{
/**
* @var User
*/
public $actor;
/**
* @var string
*/
public $extensionId;
public function __construct(User $actor, string $extensionId)
{
$this->actor = $actor;
$this->extensionId = $extensionId;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace SychO\PackageManager\Command;
use Flarum\Extension\ExtensionManager;
use SychO\PackageManager\Extension\PackageManager;
class RemoveExtensionHandler
{
/**
* @var ExtensionManager
*/
protected $extensions;
/**
* @var PackageManager
*/
protected $packages;
public function __construct(ExtensionManager $extensions, PackageManager $packages)
{
$this->extensions = $extensions;
$this->packages = $packages;
}
/**
* @throws \Flarum\User\Exception\PermissionDeniedException
* @throws \Exception
*/
public function handle(RemoveExtension $command)
{
$command->actor->assertAdmin();
$extension = $this->extensions->getExtension($command->extensionId);
if (empty($extension)) {
// ... exception
}
$this->packages->removePackage($extension->name);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace SychO\PackageManager\Command;
use Flarum\User\User;
class RequireExtension
{
/**
* @var User
*/
public $actor;
/**
* @var string
*/
public $package;
public function __construct(User $actor, string $package)
{
$this->actor = $actor;
$this->package = $package;
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace SychO\PackageManager\Command;
use Composer\Command\RequireCommand;
use Composer\Config;
use Composer\Console\Application;
use Flarum\Extension\ExtensionManager;
use Flarum\Foundation\Paths;
use Illuminate\Contracts\Console\Kernel;
use SychO\PackageManager\Extension\ExtensionUtils;
use SychO\PackageManager\Extension\PackageManager;
use SychO\PackageManager\RequirePackageValidator;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\BufferedOutput;
class RequireExtensionHandler
{
/**
* @var ExtensionManager
*/
protected $extensions;
/**
* @var PackageManager
*/
protected $packages;
/**
* @var RequireCommand
*/
protected $command;
/**
* @var RequirePackageValidator
*/
protected $validator;
public function __construct(ExtensionManager $extensions, PackageManager $packages, RequireCommand $command, RequirePackageValidator $validator)
{
$this->extensions = $extensions;
$this->packages = $packages;
$this->command = $command;
$this->validator = $validator;
}
/**
* @throws \Flarum\User\Exception\PermissionDeniedException
* @throws \Exception
*/
public function handle(RequireExtension $command)
{
$command->actor->assertAdmin();
$this->validator->assertValid(['package' => $command->package]);
$extensionId = ExtensionUtils::nameToId($command->package);
if (! empty($this->extensions->getExtension($extensionId))) {
// ... exception
}
// $this->packages->requirePackage($command->package);
$paths = resolve(Paths::class);
putenv("COMPOSER_HOME={$paths->storage}/.composer");
putenv("COMPOSER={$paths->base}/composer.json");
Config::$defaultConfig['vendor-dir'] = $paths->base.'/vendor';
@ini_set('memory_limit', '1G');
@set_time_limit(5 * 60);
$application = new Application();
$application->setAutoExit(false);
$output = new BufferedOutput();
$input = new ArrayInput([
'command' => 'require',
'packages' => [$command->package],
// '--dry-run' => true,
]);
$application->run($input, $output);
error_log('nandeeeeeeeeeeeeeee');
throw new \Exception($output->fetch());
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace SychO\PackageManager\Command;
use Flarum\User\User;
class UpdateExtension
{
/**
* @var User
*/
public $actor;
/**
* @var string
*/
public $extensionId;
public function __construct(User $actor, string $extensionId)
{
$this->actor = $actor;
$this->extensionId = $extensionId;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace SychO\PackageManager\Command;
use Flarum\Extension\ExtensionManager;
use SychO\PackageManager\Extension\PackageManager;
class UpdateExtensionHandler
{
/**
* @var ExtensionManager
*/
protected $extensions;
/**
* @var PackageManager
*/
protected $packages;
public function __construct(ExtensionManager $extensions, PackageManager $packages)
{
$this->extensions = $extensions;
$this->packages = $packages;
}
/**
* @throws \Flarum\User\Exception\PermissionDeniedException
* @throws \Exception
*/
public function handle(UpdateExtension $command)
{
$command->actor->assertAdmin();
$extension = $this->extensions->getExtension($command->extensionId);
if (empty($extension)) {
// ... exception
}
$this->packages->updatePackage($extension->name);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace SychO\PackageManager;
use Flarum\Foundation\Paths;
use Illuminate\Contracts\Container\Container;
use SychO\PackageManager\Composer\ComposerEnvironment;
use Flarum\Foundation\AbstractServiceProvider;
use Illuminate\Filesystem\Filesystem;
class ComposerEnvironmentProvider extends AbstractServiceProvider
{
public function register()
{
$this->container->singleton(ComposerEnvironment::class, function(Container $container) {
return new ComposerEnvironment(
$container->make(Paths::class)->base,
$container->make(Paths::class)->storage.'/composer-home',
$container->make(Filesystem::class),
$container->make(Paths::class)
);
});
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SychO\PackageManager\Extension\Event;
use Flarum\Extension\Extension;
class Installed
{
/**
* @var Extension
*/
public $extension;
public function __construct(Extension $extension)
{
$this->extension = $extension;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SychO\PackageManager\Extension\Event;
use Flarum\Extension\Extension;
class Removed
{
/**
* @var Extension
*/
public $extension;
public function __construct(Extension $extension)
{
$this->extension = $extension;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SychO\PackageManager\Extension\Event;
use Flarum\Extension\Extension;
class Updated
{
/**
* @var Extension
*/
public $extension;
public function __construct(Extension $extension)
{
$this->extension = $extension;
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace SychO\PackageManager\Extension;
class ExtensionUtils
{
public static function nameToId(string $name): string
{
[$vendor, $package] = explode('/', $name);
$package = str_replace(['flarum-ext-', 'flarum-'], '', $package);
return "$vendor-$package";
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace SychO\PackageManager;
use Flarum\Foundation\AbstractValidator;
class RequirePackageValidator extends AbstractValidator
{
/**
* {@inheritdoc}
*/
protected $rules = [
'package' => 'required|string'
];
}