Detect Readme of plugin automatically

This commit is contained in:
Pig Fang 2019-12-08 23:58:44 +08:00
parent 8a66a70ced
commit 66becb27d0
11 changed files with 137 additions and 25 deletions

View File

@ -24,6 +24,25 @@ class PluginController extends Controller
}
}
public function readme(PluginManager $plugins, $name)
{
$plugin = $plugins->get($name);
if (empty($plugin)) {
return abort(404, trans('admin.plugins.operations.no-readme-notice'));
}
$readmePath = $plugin->getReadme();
if (empty($readmePath)) {
return abort(404, trans('admin.plugins.operations.no-readme-notice'));
}
$title = $plugin->title;
$path = $plugin->getPath().'/'.$readmePath;
$content = resolve('parsedown')->text(file_get_contents($path));
return view('admin.plugin.readme', compact('content', 'title'));
}
public function manage(Request $request, PluginManager $plugins)
{
$name = $request->input('name');
@ -95,6 +114,7 @@ class PluginController extends Controller
'version' => $plugin->version,
'url' => $plugin->url,
'enabled' => $plugin->isEnabled(),
'readme' => (bool) $plugin->getReadme(),
'config' => $plugin->hasConfig(),
'dependencies' => [
'all' => $plugin->require,

View File

@ -9,6 +9,12 @@ use Illuminate\Support\Str;
class Plugin
{
const README_FILES = [
'README.md',
'readme.md',
'README.MD',
];
/**
* The full directory of this plugin.
*
@ -58,6 +64,13 @@ class Plugin
return "$baseUrl/{$this->name}/assets/$relativeUri?v=".$this->version;
}
public function getReadme()
{
return Arr::first(self::README_FILES, function ($filename) {
return file_exists($this->path.'/'.$filename);
});
}
public function hasConfig(): bool
{
return $this->hasConfigClass() || $this->hasConfigView();

View File

@ -11,34 +11,42 @@
<template #table-row="props">
<span v-if="props.column.field === 'title'">
<strong>{{ props.formattedRow[props.column.field] }}</strong>
<div v-if="props.row.enabled" class="actions">
<template v-if="props.row.config">
<div class="actions">
<template v-if="props.row.readme">
<a
v-t="'admin.configurePlugin'"
:href="`${baseUrl}/admin/plugins/readme/${props.row.name}`"
class="text-primary"
:href="`${baseUrl}/admin/plugins/config/${props.row.name}`"
/> |
>{{ $t('admin.pluginReadme') }}</a> |
</template>
<template v-if="props.row.enabled" class="actions">
<template v-if="props.row.config">
<a
v-t="'admin.configurePlugin'"
class="text-primary"
:href="`${baseUrl}/admin/plugins/config/${props.row.name}`"
/> |
</template>
<a
v-t="'admin.disablePlugin'"
href="#"
class="text-primary"
@click="disablePlugin(props.row)"
/>
</template>
<template v-else class="actions">
<a
v-t="'admin.enablePlugin'"
href="#"
class="text-primary"
@click="enablePlugin(props.row)"
/> |
<a
v-t="'admin.deletePlugin'"
href="#"
class="text-danger"
@click="deletePlugin(props.row)"
/>
</template>
<a
v-t="'admin.disablePlugin'"
href="#"
class="text-primary"
@click="disablePlugin(props.row)"
/>
</div>
<div v-else class="actions">
<a
v-t="'admin.enablePlugin'"
href="#"
class="text-primary"
@click="enablePlugin(props.row)"
/> |
<a
v-t="'admin.deletePlugin'"
href="#"
class="text-danger"
@click="deletePlugin(props.row)"
/>
</div>
</span>
<span v-else-if="props.column.field === 'description'">

View File

@ -169,3 +169,19 @@ test('delete plugin', async () => {
await flushPromises()
expect(wrapper.text()).toContain('No data')
})
test('readme link', async () => {
Vue.prototype.$http.get.mockResolvedValue([
{
name: 'a',
readme: true,
dependencies: { all: {}, unsatisfied: {} },
},
])
const wrapper = mount(Plugins)
await flushPromises()
const link = wrapper.find('.actions > a:nth-child(1)')
expect(link.text()).toContain('admin.pluginReadme')
expect(link.attributes('href')).toBe('/admin/plugins/readme/a')
})

View File

@ -116,6 +116,7 @@ plugins:
description: Description
author: Author
version: Version
readme: Read Me
dependencies: Dependencies
operations:
@ -129,6 +130,7 @@ plugins:
disabled: :plugin has been disabled.
deleted: The plugin was deleted successfully.
no-config-notice: The plugin is not installed or doesn't provide a configuration page.
no-readme-notice: The plugin doesn't contain a readme file.
not-found: No such plugin.
market:

View File

@ -276,6 +276,7 @@ admin:
pluginAuthor: Author
pluginVersion: Version
pluginName: Name
pluginReadme: Read Me
pluginDescription: Description
pluginDependencies: Dependencies
installPlugin: Install

View File

@ -11,6 +11,7 @@
- Added support of customizing UI text.
- Spanish support (Greatly thanks [@poopingpenis](https://github.com/poopingpenis))
- Brand new website theme color settings.
- Detect Readme file of plugin automatically.
## Tweaked

View File

@ -11,6 +11,7 @@
- 支持自定义 UI 文本
- 西班牙语支持(感谢 [@poopingpenis](https://github.com/poopingpenis)
- 全新的站点配色设置
- 自动识别插件的说明文件
## 调整

View File

@ -0,0 +1,17 @@
{% extends 'admin.base' %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">{{ trans('admin.plugins.readme') }}</h3>
</div>
<div class="card-body">{{ content|raw }}</div>
<div class="card-footer">
<button class="btn bg-primary" onclick="history.back()">
{{ trans('general.back') }}
</button>
</div>
</div>
{% endblock %}

View File

@ -151,6 +151,7 @@ Route::group(['middleware' => ['authorize', 'admin'], 'prefix' => 'admin'], func
Route::view('/manage', 'admin.plugins');
Route::post('/manage', 'PluginController@manage');
Route::any('/config/{name}', 'PluginController@config');
Route::get('/readme/{name}', 'PluginController@readme');
Route::view('/market', 'admin.market');
Route::get('/market-data', 'MarketController@marketData');

View File

@ -90,6 +90,37 @@ class PluginControllerTest extends TestCase
option(['plugins_enabled' => '[]']);
}
public function testReadme()
{
$this->mock(PluginManager::class, function ($mock) {
$mock->shouldReceive('getEnabledPlugins')->andReturn(collect());
$mock->shouldReceive('get')
->with('fake1')
->once()
->andReturn(null);
$mock->shouldReceive('get')
->with('fake2')
->once()
->andReturn(new Plugin(storage_path(), []));
$mock->shouldReceive('get')
->with('fake3')
->once()
->andReturn(new Plugin(base_path(), ['title' => '']));
});
// No such plugin.
$this->get('/admin/plugins/readme/fake1')->assertNotFound();
// Plugin doesn't have readme.
$this->get('/admin/plugins/readme/fake2')->assertNotFound();
// Ok.
$this->get('/admin/plugins/readme/fake3')->assertSuccessful();
}
public function testManage()
{
$this->mock(PluginManager::class, function ($mock) {
@ -255,6 +286,7 @@ class PluginControllerTest extends TestCase
'url',
'enabled',
'config',
'readme',
'dependencies',
],
]);