OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
55
app/Domain/CsvImport/Controllers/Upload.php
Normal file
55
app/Domain/CsvImport/Controllers/Upload.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\CsvImport\Controllers;
|
||||
|
||||
use League\Csv\Exception;
|
||||
use Leantime\Core\Controller\Controller;
|
||||
use Leantime\Domain\Auth\Models\Roles;
|
||||
use Leantime\Domain\Auth\Services\Auth;
|
||||
use Leantime\Domain\CsvImport\Services\CsvImport as CsvImportService;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* upload controller for csvImport plugin
|
||||
*/
|
||||
class Upload extends Controller
|
||||
{
|
||||
private CsvImportService $providerService;
|
||||
|
||||
/**
|
||||
* constructor - initialize private variables
|
||||
*/
|
||||
public function init(CsvImportService $providerService): void
|
||||
{
|
||||
Auth::authOrRedirect([Roles::$owner, Roles::$admin, Roles::$manager, Roles::$editor]);
|
||||
|
||||
$this->providerService = $providerService;
|
||||
}
|
||||
|
||||
/**
|
||||
* get - display upload form
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get(): Response
|
||||
{
|
||||
return $this->tpl->displayPartial('csvImport.upload');
|
||||
}
|
||||
|
||||
/**
|
||||
* post - process uploaded file
|
||||
*/
|
||||
public function post(array $params): Response
|
||||
{
|
||||
$file = $this->incomingRequest->file('file');
|
||||
|
||||
try {
|
||||
$id = $this->providerService->processUpload($file);
|
||||
} catch (Exception $e) {
|
||||
return $this->tpl->displayJson(json_encode(['error' => $e->getMessage()]), 500);
|
||||
}
|
||||
|
||||
return $this->tpl->displayJson(json_encode(['id' => $id]));
|
||||
}
|
||||
}
|
||||
24
app/Domain/CsvImport/Listeners/AddCSVImportProvider.php
Normal file
24
app/Domain/CsvImport/Listeners/AddCSVImportProvider.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\CsvImport\Listeners;
|
||||
|
||||
use Leantime\Domain\CsvImport\Services;
|
||||
|
||||
/**
|
||||
* Class AddCSVImportProvider
|
||||
*
|
||||
* The AddCSVImportProvider class is responsible for adding a CSV import provider to the given payload.
|
||||
*/
|
||||
class AddCSVImportProvider
|
||||
{
|
||||
/**
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException
|
||||
*/
|
||||
public function handle(mixed $payload): mixed
|
||||
{
|
||||
$provider = app()->make(Services\CsvImport::class);
|
||||
$payload[$provider->id] = $provider;
|
||||
|
||||
return $payload;
|
||||
}
|
||||
}
|
||||
171
app/Domain/CsvImport/Services/CsvImport.php
Normal file
171
app/Domain/CsvImport/Services/CsvImport.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\CsvImport\Services;
|
||||
|
||||
use League\Csv\Exception as CsvException;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
use Leantime\Core\Controller\Frontcontroller;
|
||||
use Leantime\Domain\Connector\Models\Entity;
|
||||
use Leantime\Domain\Connector\Models\Integration;
|
||||
use Leantime\Domain\Connector\Models\Provider;
|
||||
use Leantime\Domain\Connector\Services\Integrations;
|
||||
use Leantime\Domain\Connector\Services\ProviderIntegration;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CsvImport extends Provider implements ProviderIntegration
|
||||
{
|
||||
/**
|
||||
* @var array|array[]
|
||||
*/
|
||||
public array $entities;
|
||||
|
||||
public array $methods;
|
||||
|
||||
public array $steps = [
|
||||
'connect',
|
||||
'entity',
|
||||
'fields',
|
||||
'parse',
|
||||
'import',
|
||||
];
|
||||
|
||||
public array $button = [
|
||||
'url' => '',
|
||||
'text' => 'Import CSV',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor - initializes provider metadata and dependencies.
|
||||
*
|
||||
* @param Integrations $integrationService Connector integrations service used to persist the integration.
|
||||
*/
|
||||
public function __construct(private Integrations $integrationService)
|
||||
{
|
||||
|
||||
$this->id = 'csv_importer';
|
||||
$this->name = 'CSV Import';
|
||||
$this->image = '/dist/images/svg/csv-icon.svg';
|
||||
$this->description = "Import data from a CSV file. To learn more about the CSV format, please visit our <a href='https://support.leantime.io/en/article/importing-data-via-csv-1v941gy' target='_blank'>documentation</a>";
|
||||
|
||||
$this->methods[] = 'import, update';
|
||||
|
||||
// CSVs can be anyting but are always one file.
|
||||
$this->entities = [
|
||||
'default' => [
|
||||
'name' => 'Sheet',
|
||||
'fields' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->button['url'] = BASE_URL.'/connector/integration?provider='.$this->id.'#/csvImport/upload';
|
||||
}
|
||||
|
||||
// Logic to connect to provider goes here.
|
||||
// Needs to manage new connection as well as existing connections.
|
||||
// Should return bool so we can drive logic in the frontend.
|
||||
public function connect(): Response
|
||||
{
|
||||
// Connection done. Send to next step.
|
||||
// May just want to add a nextStep() method to provider model or so.
|
||||
return Frontcontroller::redirect(BASE_URL.'/connector/integration?provider='.$this->id.'#/csvImport/upload');
|
||||
}
|
||||
|
||||
// Sync the entities from the db
|
||||
|
||||
/**
|
||||
* @return true
|
||||
*/
|
||||
public function sync(Entity $Entity): bool
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get available fields
|
||||
|
||||
/**
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getFields(): mixed
|
||||
{
|
||||
return session('csvImporter.headers') ?? [];
|
||||
}
|
||||
|
||||
public function setFields(array $fields): void {}
|
||||
|
||||
// Get available entities
|
||||
public function getEntities(): array
|
||||
{
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, mixed>|false
|
||||
*/
|
||||
public function getValues(Entity $Entity): mixed
|
||||
{
|
||||
$integrationMeta = session('csvImporter.meta', '');
|
||||
|
||||
if (empty($integrationMeta)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rows = safe_unserialize($integrationMeta, []);
|
||||
|
||||
// Removing the first row if it contains headers
|
||||
// can be returned or dealt with later on for field matching
|
||||
if (count($rows) > 0) {
|
||||
$headers = array_shift($rows);
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function geValues()
|
||||
{
|
||||
|
||||
return session('csv_records') ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an uploaded CSV file, store its records in the session and create a
|
||||
* Connector integration record built from the CSV header.
|
||||
*
|
||||
* Reads the uploaded CSV (with the first row as header), materializes all
|
||||
* records into the session under the `csv_records` key, builds an
|
||||
* Integration model whose `fields` are the comma separated header columns
|
||||
* and persists it via the Connector integrations service.
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param UploadedFile $file The uploaded CSV file.
|
||||
* @return int The id of the created integration record.
|
||||
*
|
||||
* @throws CsvException When the CSV cannot be parsed.
|
||||
*/
|
||||
public function processUpload(UploadedFile $file): int
|
||||
{
|
||||
$csv = Reader::createFromPath($file->getRealPath(), 'r');
|
||||
$csv->setHeaderOffset(0);
|
||||
|
||||
// Will throw a League\Csv\Exception if the CSV is malformed.
|
||||
$records = Statement::create()->process($csv);
|
||||
|
||||
$header = $records->getHeader();
|
||||
|
||||
$rows = [];
|
||||
foreach ($records as $record) {
|
||||
$rows[] = $record;
|
||||
}
|
||||
|
||||
// Temporarily store the parsed records in the session for later steps.
|
||||
session(['csv_records' => $rows]);
|
||||
|
||||
$integration = new Integration;
|
||||
$integration->fields = implode(',', $header);
|
||||
|
||||
return (int) $this->integrationService->create($integration);
|
||||
}
|
||||
}
|
||||
108
app/Domain/CsvImport/Templates/upload.blade.php
Normal file
108
app/Domain/CsvImport/Templates/upload.blade.php
Normal file
@@ -0,0 +1,108 @@
|
||||
@extends($layout)
|
||||
|
||||
@section('content')
|
||||
|
||||
@php
|
||||
$maxSize = \Leantime\Core\Files\FileManager::getMaximumFileUploadSize();
|
||||
@endphp
|
||||
|
||||
<div id="fileManager">
|
||||
|
||||
{!! $tpl->displayNotification() !!}
|
||||
|
||||
<h2>Upload CSV file</h2>
|
||||
<p>You can upload CSVs to import or update Tasks, Projects, Goals. <a href="https://support.leantime.io/importing-data-via-csv" target="_blank">Check our documentation</a> to learn more about the formatting and to download templates</p>
|
||||
<br /><br/>
|
||||
<div class="uploadWrapper" style="width:100%">
|
||||
|
||||
<form id="upload-form">
|
||||
|
||||
<div class="extra" style="margin-top:5px;"></div>
|
||||
<div class="fileUploadDrop">
|
||||
<p><i>{!! __('text.drop_files') !!}</i></p>
|
||||
<div class="file-upload-input" style="margin:auto; display:inline-block"></div>
|
||||
</div>
|
||||
|
||||
<!-- Progress bar #1 -->
|
||||
<div class="input-progress"></div>
|
||||
|
||||
<div class="input-error"></div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@once
|
||||
<script>
|
||||
|
||||
if (typeof uppy === 'undefined') {
|
||||
|
||||
|
||||
const uppy = new Uppy.Uppy({
|
||||
debug: false,
|
||||
autoProceed: true,
|
||||
restrictions: {
|
||||
maxFileSize: {{ $maxSize }}
|
||||
}
|
||||
});
|
||||
|
||||
uppy.use(Uppy.DropTarget, { target: '#fileManager' });
|
||||
|
||||
uppy.use(Uppy.FileInput, {
|
||||
target: '.file-upload-input',
|
||||
pretty: true,
|
||||
locale: {
|
||||
strings: {
|
||||
chooseFiles: ' Browse',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
uppy.use(Uppy.XHRUpload, {
|
||||
endpoint: '{{ BASE_URL }}/csvImport/upload',
|
||||
formData: true,
|
||||
fieldName: 'file'
|
||||
});
|
||||
|
||||
uppy.use(Uppy.StatusBar, {
|
||||
target: '.input-progress',
|
||||
hideUploadButton: false,
|
||||
hideAfterFinish: false,
|
||||
});
|
||||
|
||||
uppy.use(Uppy.Form, { target: '#upload-form' });
|
||||
|
||||
// Upload
|
||||
uppy.on("restriction-failed", (file, error) => {
|
||||
|
||||
jQuery(".input-error").html("<span class='label-important'>"+error+"</span>");
|
||||
return false
|
||||
});
|
||||
|
||||
uppy.on('upload-success', (file, response) => {
|
||||
|
||||
jQuery(".input-error").text('');
|
||||
|
||||
window.location.href = "{{ BASE_URL }}/connector/integration?provider=csv_importer&step=entity&integrationId="+response.body.id;
|
||||
|
||||
});
|
||||
|
||||
|
||||
uppy.on('upload-error', (file, error, response) => {
|
||||
|
||||
jQuery(".input-error").html("<span class='label-important'>There is a problem with your CSV file: "+response.body.error+"</span>");
|
||||
|
||||
|
||||
|
||||
return false
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
@endonce
|
||||
|
||||
@endsection
|
||||
15
app/Domain/CsvImport/register.php
Normal file
15
app/Domain/CsvImport/register.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use Leantime\Core\Events\EventDispatcher;
|
||||
|
||||
// Register event listener
|
||||
EventDispatcher::add_filter_listener(
|
||||
'leantime.domain.connector.services.providers.loadProviders.providerList',
|
||||
function (mixed $payload) {
|
||||
|
||||
$provider = app()->make(\Leantime\Domain\CsvImport\Services\CsvImport::class);
|
||||
$payload[$provider->id] = $provider;
|
||||
|
||||
return $payload;
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user