parent
eb62e04c1c
commit
f9b32c8e69
|
|
@ -82,6 +82,15 @@ class MarketController extends Controller
|
||||||
return json(trans('admin.plugins.market.non-existent', ['plugin' => $name]), 1);
|
return json(trans('admin.plugins.market.non-existent', ['plugin' => $name]), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$fakePlugin = new Plugin('', $metadata);
|
||||||
|
$unsatisfied = $manager->getUnsatisfied($fakePlugin);
|
||||||
|
$conflicts = $manager->getConflicts($fakePlugin);
|
||||||
|
if ($unsatisfied->isNotEmpty() || $conflicts->isNotEmpty()) {
|
||||||
|
$reason = $manager->formatUnresolved($unsatisfied, $conflicts);
|
||||||
|
|
||||||
|
return json(trans('admin.plugins.market.unresolved'), 1, compact('reason'));
|
||||||
|
}
|
||||||
|
|
||||||
$url = $metadata['dist']['url'];
|
$url = $metadata['dist']['url'];
|
||||||
$filename = Arr::last(explode('/', $url));
|
$filename = Arr::last(explode('/', $url));
|
||||||
$pluginsDir = $manager->getPluginsDirs()->first();
|
$pluginsDir = $manager->getPluginsDirs()->first();
|
||||||
|
|
|
||||||
|
|
@ -59,27 +59,7 @@ class PluginController extends Controller
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
return json(trans('admin.plugins.operations.enabled', ['plugin' => $plugin->title]), 0);
|
return json(trans('admin.plugins.operations.enabled', ['plugin' => $plugin->title]), 0);
|
||||||
} else {
|
} else {
|
||||||
$unsatisfied = $result['unsatisfied']->map(function ($detail, $name) use ($plugins) {
|
$reason = $plugins->formatUnresolved($result['unsatisfied'], $result['conflicts']);
|
||||||
$constraint = $detail['constraint'];
|
|
||||||
if (! $detail['version']) {
|
|
||||||
$plugin = $plugins->get($name);
|
|
||||||
$name = $plugin ? trans($plugin->title) : $name;
|
|
||||||
|
|
||||||
return trans('admin.plugins.operations.unsatisfied.disabled', compact('name'));
|
|
||||||
} else {
|
|
||||||
$title = trans($plugins->get($name)->title);
|
|
||||||
|
|
||||||
return trans('admin.plugins.operations.unsatisfied.version', compact('title', 'constraint'));
|
|
||||||
}
|
|
||||||
})->values()->all();
|
|
||||||
|
|
||||||
$conflicts = $result['conflicts']->map(function ($detail, $name) use ($plugins) {
|
|
||||||
$title = trans($plugins->get($name)->title);
|
|
||||||
|
|
||||||
return trans('admin.plugins.operations.unsatisfied.conflict', compact('title'));
|
|
||||||
})->values()->all();
|
|
||||||
|
|
||||||
$reason = array_merge($unsatisfied, $conflicts);
|
|
||||||
|
|
||||||
return json(trans('admin.plugins.operations.unsatisfied.notice'), 1, compact('reason'));
|
return json(trans('admin.plugins.operations.unsatisfied.notice'), 1, compact('reason'));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,36 @@ class PluginManager
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the "unresolved" information into human-readable text.
|
||||||
|
*/
|
||||||
|
public function formatUnresolved(
|
||||||
|
Collection $unsatisfied,
|
||||||
|
Collection $conflicts
|
||||||
|
): array {
|
||||||
|
$unsatisfied = $unsatisfied->map(function ($detail, $name) {
|
||||||
|
$constraint = $detail['constraint'];
|
||||||
|
if (! $detail['version']) {
|
||||||
|
$plugin = $this->get($name);
|
||||||
|
$name = $plugin ? trans($plugin->title) : $name;
|
||||||
|
|
||||||
|
return trans('admin.plugins.operations.unsatisfied.disabled', compact('name'));
|
||||||
|
} else {
|
||||||
|
$title = trans($this->get($name)->title);
|
||||||
|
|
||||||
|
return trans('admin.plugins.operations.unsatisfied.version', compact('title', 'constraint'));
|
||||||
|
}
|
||||||
|
})->values()->all();
|
||||||
|
|
||||||
|
$conflicts = $conflicts->map(function ($detail, $name) {
|
||||||
|
$title = trans($this->get($name)->title);
|
||||||
|
|
||||||
|
return trans('admin.plugins.operations.unsatisfied.conflict', compact('title'));
|
||||||
|
})->values()->all();
|
||||||
|
|
||||||
|
return array_merge($unsatisfied, $conflicts);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plugins path.
|
* The plugins path.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { showModal } from '../../scripts/notify'
|
||||||
|
|
||||||
|
export default function (message: string, reason: string[]): void {
|
||||||
|
const div = document.createElement('div')
|
||||||
|
const p = document.createElement('p')
|
||||||
|
p.textContent = message
|
||||||
|
div.appendChild(p)
|
||||||
|
const ul = document.createElement('ul')
|
||||||
|
reason.forEach(item => {
|
||||||
|
const li = document.createElement('li')
|
||||||
|
li.textContent = item
|
||||||
|
ul.appendChild(li)
|
||||||
|
})
|
||||||
|
div.appendChild(ul)
|
||||||
|
showModal({
|
||||||
|
mode: 'alert',
|
||||||
|
dangerousHTML: div.outerHTML,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { showModal, toast } from '../../scripts/notify'
|
import { showModal, toast } from '../../scripts/notify'
|
||||||
|
import alertUnresolvedPlugins from './alertUnresolvedPlugins'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
data: () => ({ plugins: [] }),
|
data: () => ({ plugins: [] }),
|
||||||
|
|
@ -32,21 +33,7 @@ export default Vue.extend({
|
||||||
toast.success(message)
|
toast.success(message)
|
||||||
this.$set(this.plugins[originalIndex], 'enabled', true)
|
this.$set(this.plugins[originalIndex], 'enabled', true)
|
||||||
} else {
|
} else {
|
||||||
const div = document.createElement('div')
|
alertUnresolvedPlugins(message, reason)
|
||||||
const p = document.createElement('p')
|
|
||||||
p.textContent = message
|
|
||||||
div.appendChild(p)
|
|
||||||
const ul = document.createElement('ul')
|
|
||||||
reason.forEach(item => {
|
|
||||||
const li = document.createElement('li')
|
|
||||||
li.textContent = item
|
|
||||||
ul.appendChild(li)
|
|
||||||
})
|
|
||||||
div.appendChild(ul)
|
|
||||||
showModal({
|
|
||||||
mode: 'alert',
|
|
||||||
dangerousHTML: div.outerHTML,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { VueGoodTable } from 'vue-good-table'
|
import { VueGoodTable } from 'vue-good-table'
|
||||||
import 'vue-good-table/dist/vue-good-table.min.css'
|
import 'vue-good-table/dist/vue-good-table.min.css'
|
||||||
|
import alertUnresolvedPlugins from '../../components/mixins/alertUnresolvedPlugins'
|
||||||
import enablePlugin from '../../components/mixins/enablePlugin'
|
import enablePlugin from '../../components/mixins/enablePlugin'
|
||||||
import tableOptions from '../../components/mixins/tableOptions'
|
import tableOptions from '../../components/mixins/tableOptions'
|
||||||
import emitMounted from '../../components/mixins/emitMounted'
|
import emitMounted from '../../components/mixins/emitMounted'
|
||||||
|
|
@ -134,7 +135,11 @@ export default {
|
||||||
async installPlugin({ name, originalIndex }) {
|
async installPlugin({ name, originalIndex }) {
|
||||||
this.installing = name
|
this.installing = name
|
||||||
|
|
||||||
const { code, message } = await this.$http.post(
|
const {
|
||||||
|
code,
|
||||||
|
message,
|
||||||
|
data,
|
||||||
|
} = await this.$http.post(
|
||||||
'/admin/plugins/market/download',
|
'/admin/plugins/market/download',
|
||||||
{ name },
|
{ name },
|
||||||
)
|
)
|
||||||
|
|
@ -142,6 +147,8 @@ export default {
|
||||||
toast.success(message)
|
toast.success(message)
|
||||||
this.plugins[originalIndex].update_available = false
|
this.plugins[originalIndex].update_available = false
|
||||||
this.plugins[originalIndex].installed = true
|
this.plugins[originalIndex].installed = true
|
||||||
|
} else if (data && data.reason) {
|
||||||
|
alertUnresolvedPlugins(message, data.reason)
|
||||||
} else {
|
} else {
|
||||||
toast.error(message)
|
toast.error(message)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ test('install plugin', async () => {
|
||||||
])
|
])
|
||||||
Vue.prototype.$http.post
|
Vue.prototype.$http.post
|
||||||
.mockResolvedValueOnce({ code: 1, message: '1' })
|
.mockResolvedValueOnce({ code: 1, message: '1' })
|
||||||
|
.mockResolvedValueOnce({
|
||||||
|
code: 1,
|
||||||
|
message: 'unresolved',
|
||||||
|
data: { reason: ['u'] },
|
||||||
|
})
|
||||||
.mockResolvedValueOnce({ code: 0, message: '0' })
|
.mockResolvedValueOnce({ code: 0, message: '0' })
|
||||||
const wrapper = mount(Market)
|
const wrapper = mount(Market)
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
@ -68,6 +73,11 @@ test('install plugin', async () => {
|
||||||
'/admin/plugins/market/download',
|
'/admin/plugins/market/download',
|
||||||
{ name: 'd' },
|
{ name: 'd' },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
button.trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
expect(showModal).toBeCalledWith(expect.objectContaining({ mode: 'alert' }))
|
||||||
|
|
||||||
button.trigger('click')
|
button.trigger('click')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
expect(wrapper.text()).toContain('admin.enablePlugin')
|
expect(wrapper.text()).toContain('admin.enablePlugin')
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ plugins:
|
||||||
not-found: No such plugin.
|
not-found: No such plugin.
|
||||||
|
|
||||||
market:
|
market:
|
||||||
|
unresolved: There are conflicts or unsatisfied dependencies in the plugin, therefore we can't download it. Please install or update the plugins listed below, and disable those have conflicts.
|
||||||
connection-error: Unable to connect to the plugins registry. :error
|
connection-error: Unable to connect to the plugins registry. :error
|
||||||
non-existent: The plugin :plugin does not exist.
|
non-existent: The plugin :plugin does not exist.
|
||||||
install-success: Plugin was installed.
|
install-success: Plugin was installed.
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ plugins:
|
||||||
no-readme-notice: The plugin doesn't contain a readme file.
|
no-readme-notice: The plugin doesn't contain a readme file.
|
||||||
not-found: No existe dicho plugin.
|
not-found: No existe dicho plugin.
|
||||||
market:
|
market:
|
||||||
|
unresolved: Hay conflictos o dependencias insatisfechas en el plugin, por lo tanto no podemos descargar. Por favor, instale o actualice los plugins listados a continuación, y desactive los que tienen conflictos.
|
||||||
connection-error: No se puede conectar al registro de plugins. :error
|
connection-error: No se puede conectar al registro de plugins. :error
|
||||||
non-existent: El plugin :plugin no existe.
|
non-existent: El plugin :plugin no existe.
|
||||||
install-success: Se instaló el plugin.
|
install-success: Se instaló el plugin.
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ plugins:
|
||||||
no-readme-notice: 这个插件没有说明文件。
|
no-readme-notice: 这个插件没有说明文件。
|
||||||
not-found: 插件不存在
|
not-found: 插件不存在
|
||||||
market:
|
market:
|
||||||
|
unresolved: 无法下载此插件,因为其仍有冲突或未满足的依赖关系。请检查以下插件的版本,更新或安装它们并禁用存在冲突的插件:
|
||||||
connection-error: 无法连接至插件市场源,错误信息::error
|
connection-error: 无法连接至插件市场源,错误信息::error
|
||||||
non-existent: 插件 :plugin 不存在
|
non-existent: 插件 :plugin 不存在
|
||||||
install-success: 插件安装成功
|
install-success: 插件安装成功
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
- Fixed fallback when unknown locale is detected.
|
- Fixed fallback when unknown locale is detected.
|
||||||
- Fixed compatibility with PHP 7.4.
|
- Fixed compatibility with PHP 7.4.
|
||||||
- Fixed the display problem for too long texture name.
|
- Fixed the display problem for too long texture name.
|
||||||
|
- Fixed that dependencies and conflicts haven't been checked before installing plugin.
|
||||||
|
|
||||||
## Removed
|
## Removed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
- 修复不能针对未知语言进行降级的问题
|
- 修复不能针对未知语言进行降级的问题
|
||||||
- 与 PHP 7.4 的兼容问题
|
- 与 PHP 7.4 的兼容问题
|
||||||
- 材质名过长时的显示问题
|
- 材质名过长时的显示问题
|
||||||
|
- 下载插件前不检查依赖和冲突的问题
|
||||||
|
|
||||||
## 移除
|
## 移除
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,26 @@ class MarketControllerTest extends TestCase
|
||||||
'message' => trans('admin.plugins.market.non-existent', ['plugin' => 'non-existent-plugin']),
|
'message' => trans('admin.plugins.market.non-existent', ['plugin' => 'non-existent-plugin']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Unresolved plugin.
|
||||||
|
$fakeRegistry = json_encode(['packages' => [
|
||||||
|
[
|
||||||
|
'name' => 'fake',
|
||||||
|
'version' => '0.0.0',
|
||||||
|
'require' => ['a' => '^4.0.0'],
|
||||||
|
],
|
||||||
|
]]);
|
||||||
|
$this->appendToGuzzleQueue([new Response(200, [], $fakeRegistry)]);
|
||||||
|
$this->postJson('/admin/plugins/market/download', ['name' => 'fake'])
|
||||||
|
->assertJson([
|
||||||
|
'message' => trans('admin.plugins.market.unresolved'),
|
||||||
|
'code' => 1,
|
||||||
|
'data' => [
|
||||||
|
'reason' => [
|
||||||
|
trans('admin.plugins.operations.unsatisfied.disabled', ['name' => 'a']),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
// Download
|
// Download
|
||||||
$fakeRegistry = json_encode(['packages' => [
|
$fakeRegistry = json_encode(['packages' => [
|
||||||
[
|
[
|
||||||
|
|
@ -51,9 +71,8 @@ class MarketControllerTest extends TestCase
|
||||||
->once()
|
->once()
|
||||||
->andThrow(new \Exception());
|
->andThrow(new \Exception());
|
||||||
});
|
});
|
||||||
$this->postJson('/admin/plugins/market/download', [
|
$this->postJson('/admin/plugins/market/download', ['name' => 'fake'])
|
||||||
'name' => 'fake',
|
->assertJson(['code' => 1]);
|
||||||
])->assertJson(['code' => 1]);
|
|
||||||
|
|
||||||
$this->appendToGuzzleQueue([new Response(200, [], $fakeRegistry)]);
|
$this->appendToGuzzleQueue([new Response(200, [], $fakeRegistry)]);
|
||||||
$this->mock(PackageManager::class, function ($mock) {
|
$this->mock(PackageManager::class, function ($mock) {
|
||||||
|
|
@ -65,9 +84,11 @@ class MarketControllerTest extends TestCase
|
||||||
->with(base_path('plugins'))
|
->with(base_path('plugins'))
|
||||||
->once();
|
->once();
|
||||||
});
|
});
|
||||||
$this->postJson('/admin/plugins/market/download', [
|
$this->postJson('/admin/plugins/market/download', ['name' => 'fake'])
|
||||||
'name' => 'fake',
|
->assertJson([
|
||||||
])->assertJson(['code' => 0, 'message' => trans('admin.plugins.market.install-success')]);
|
'code' => 0,
|
||||||
|
'message' => trans('admin.plugins.market.install-success')
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCheckUpdates()
|
public function testCheckUpdates()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user