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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 117 additions & 81 deletions ProcessMaker/Http/Controllers/Api/DevLinkController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
namespace ProcessMaker\Http\Controllers\Api;

use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Notification;
use Illuminate\Validation\Rule;
use ProcessMaker\Events\CustomizeUiUpdated;
use ProcessMaker\Exception\ValidationException;
use ProcessMaker\Http\Controllers\Controller;
use ProcessMaker\Http\Requests\DevLinkStoreRequest;
use ProcessMaker\Http\Resources\ApiCollection;
use ProcessMaker\Jobs\CompileSass;
use ProcessMaker\Jobs\CompileUI;
use ProcessMaker\Jobs\DevLinkInstall;
use ProcessMaker\Models\Bundle;
Expand All @@ -23,6 +24,8 @@
use ProcessMaker\Models\SettingsMenus;
use ProcessMaker\Models\User;
use ProcessMaker\Notifications\BundleUpdatedNotification;
use ProcessMaker\Package\PackageDynamicUI\Models\Dashboard;
use ProcessMaker\Package\PackageDynamicUI\Models\Menu;

class DevLinkController extends Controller
{
Expand Down Expand Up @@ -56,27 +59,38 @@ public function show(DevLink $devLink)
return $devLink;
}

public function store(Request $request)
public function store(DevLinkStoreRequest $request)
{
$request->validate([
'name' => ['required'],
'url' => ['required', 'url'],
]);
$devLink = DevLink::where('name', $request->input('name'))->first();
if ($devLink) {
$devLink->url = $request->input('url');
} else {
$devLink = new DevLink();
$devLink->name = $request->input('name');
$devLink->url = $request->input('url');
$normalizedUrl = $this->normalizeUrl($request->input('url'));
$existingDevLink = DevLink::query()
->get(['id', 'name', 'url'])
->first(fn (DevLink $devLink) => $this->normalizeUrl($devLink->url) === $normalizedUrl);
if ($existingDevLink) {
throw ValidationException::withMessages([
'url' => __(
'This instance is already linked as :name. Open or reconnect the existing connection.',
['name' => $existingDevLink->name]
),
]);
}

$devLink = new DevLink();
$devLink->name = $request->input('name');
$devLink->url = $normalizedUrl;
$devLink->saveOrFail();

return $devLink;
}

public function update(Request $request, DevLink $devLink)
{
$request->merge([
'name' => trim((string) $request->input('name')),
]);
$request->validate([
'name' => ['required', 'string', Rule::unique('dev_links', 'name')->ignore($devLink->id)],
]);

$devLink->name = $request->input('name');
$devLink->saveOrFail();

Expand Down Expand Up @@ -180,7 +194,7 @@ public function increaseBundleVersion(Bundle $bundle)
return $bundle;
}

public function bundleUpdated($bundleId, $token)
public function bundleUpdated(int $bundleId, string $token)
{
try {
$bundle = Bundle::where('remote_id', $bundleId)->firstOrFail();
Expand Down Expand Up @@ -208,7 +222,7 @@ public function deleteBundle(Bundle $bundle)
$bundle->delete();
}

public function installRemoteBundle(Request $request, DevLink $devLink, $remoteBundleId)
public function installRemoteBundle(Request $request, DevLink $devLink, int $remoteBundleId)
{
$updateType = $request->input('updateType', DevLinkInstall::MODE_UPDATE);
DevLinkInstall::dispatch(
Expand Down Expand Up @@ -342,7 +356,7 @@ public function addSharedAsset(Request $request)
$sharedAsset->saveOrFail();
}

public function removeSharedAsset($id)
public function removeSharedAsset(int $id)
{
$deleted = Setting::destroy($id);

Expand Down Expand Up @@ -371,9 +385,41 @@ public function installRemoteAsset(Request $request, DevLink $devLink)
];
}

public function remoteBundleVersion(DevLink $devLink, $remoteBundleId)
public function remoteBundleVersion(DevLink $devLink, int $remoteBundleId)
{
try {
$payload = $devLink->remoteBundle($remoteBundleId)->json();
} catch (RequestException|ConnectionException $e) {
return [
'available' => false,
'version' => null,
];
}

if (!is_array($payload)) {
return [
'available' => false,
'version' => null,
];
}

$payload['available'] = true;

return $payload;
}

private function normalizeUrl(string $url): string
{
return $devLink->remoteBundle($remoteBundleId);
$parts = parse_url(trim($url));
$scheme = strtolower($parts['scheme']);
$host = strtolower($parts['host']);
$port = $parts['port'] ?? null;

if (($scheme === 'http' && $port === 80) || ($scheme === 'https' && $port === 443)) {
$port = null;
}

return $scheme . '://' . $host . ($port === null ? '' : ':' . $port);
}

public function deleteBundleAsset(BundleAsset $bundleAsset)
Expand All @@ -390,80 +436,70 @@ public function deleteBundleSetting(BundleSetting $bundleSetting)
return response()->json(['message' => 'Bundle setting deleted.'], 200);
}

public function getBundleSetting(Bundle $bundle, $settingKey)
public function getBundleSetting(Bundle $bundle, string $settingKey)
{
return $bundle->settings()->where('setting', $settingKey)->first();
}

public function getBundleSettingPreview(Bundle $bundle, string $settingKey): array
{
$setting = $bundle->settings()->where('setting', $settingKey)->first();
abort_unless(in_array($settingKey, ['ui_dashboards', 'ui_menus'], true), 404);

return $setting;
return $bundle->settingPreview($settingKey);
}

public function getBundleAllSettings($settingKey)
public function getBundleAllSettings(string $settingKey)
{
if ($settingKey === 'ui_settings') {
return Setting::whereIn('key', ['css-override', 'login-footer', 'logo-alt-text'])
->get();
return match ($settingKey) {
'ui_dashboards' => $this->getAvailableDashboards(),
'ui_menus' => $this->getAvailableMenus(),
'ui_settings' => Setting::whereIn('key', ['css-override', 'login-footer', 'logo-alt-text'])->get(),
default => Setting::where([
['group_id', SettingsMenus::getId($settingKey)],
['hidden', 0],
])->get(),
};
}

private function getAvailableDashboards()
{
if (!class_exists(Dashboard::class)) {
return [];
}

return Setting::where([
['group_id', SettingsMenus::getId($settingKey)],
['hidden', 0],
])->get();
return Dashboard::query()
->orderBy('title')
->get(['id', 'title'])
->map(function (Dashboard $dashboard) {
return [
'key' => $dashboard->id,
'name' => $dashboard->title,
];
})
->values();
}

public function refreshUi()
private function getAvailableMenus()
{
CompileUI::dispatch(auth()->user()?->id);
CustomizeUiUpdated::dispatch([], [], false);
if (!class_exists(Menu::class)) {
return [];
}

return Menu::query()
->orderBy('name')
->get(['id', 'name'])
->map(function (Menu $menu) {
return [
'key' => $menu->id,
'name' => $menu->name,
];
})
->values();
}

private function writeColors($data)
public function refreshUi(Request $request)
{
// Now generate the _colors.scss file
$contents = "// Changed theme colors\n";
foreach ($data as $value) {
$contents .= $value->id . ': ' . $value->value . ";\n";
}
File::put(app()->resourcePath('sass') . '/_colors.scss', $contents);
}

/**
* Write variables font in file
*
* @param $sansSerif
* @param $serif
*/
private function writeFonts($sansSerif)
{
$sansSerif = $sansSerif ? $sansSerif : $this->sansSerifFontDefault();
// Generate the _fonts.scss file
$contents = "// Changed theme fonts\n";
$contents .= '$font-family-sans-serif: ' . $sansSerif['id'] . " !default;\n";
File::put(app()->resourcePath('sass') . '/_fonts.scss', $contents);
}

/**
* run jobs compile
*/
private function compileSass($userId)
{
// Compile the Sass files
$this->dispatch(new CompileSass([
'tag' => 'sidebar',
'origin' => 'resources/sass/sidebar/sidebar.scss',
'target' => 'public/css/sidebar.css',
'user' => $userId,
]));
$this->dispatch(new CompileSass([
'tag' => 'app',
'origin' => 'resources/sass/app.scss',
'target' => 'public/css/app.css',
'user' => $userId,
]));
$this->dispatch(new CompileSass([
'tag' => 'queues',
'origin' => 'resources/sass/admin/queues.scss',
'target' => 'public/css/admin/queues.css',
'user' => $userId,
]));
CompileUI::dispatch($request->user()?->id);
CustomizeUiUpdated::dispatch([], [], false);
}
}
31 changes: 31 additions & 0 deletions ProcessMaker/Http/Requests/DevLinkStoreRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace ProcessMaker\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use ProcessMaker\Rules\HttpOrigin;

class DevLinkStoreRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}

protected function prepareForValidation(): void
{
$this->merge([
'name' => trim((string) $this->input('name')),
'url' => trim((string) $this->input('url')),
]);
}

public function rules(): array
{
return [
'name' => ['required', 'string', Rule::unique('dev_links', 'name')],
'url' => ['bail', 'required', 'url', new HttpOrigin()],
];
}
}
5 changes: 4 additions & 1 deletion ProcessMaker/ImportExport/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ class Options

public $options;

public function __construct(array $options)
public ?int $importingUserId;

public function __construct(array $options, ?int $importingUserId = null)
{
$this->options = $options;
$this->importingUserId = $importingUserId;
}

public function get($name, $uuid)
Expand Down
Loading
Loading