diff --git a/app/Classes/FTN/Tic.php b/app/Classes/FTN/Tic.php new file mode 100644 index 0000000..ba019a9 --- /dev/null +++ b/app/Classes/FTN/Tic.php @@ -0,0 +1,227 @@ + TRUE, + 'areadesc' => FALSE, + 'ORIGIN' => TRUE, + 'FROM' => TRUE, + 'to' => FALSE, + 'FILE' => TRUE, // 8.3 DOS format + 'lfile' => FALSE, // alias fullname + 'fullname' => FALSE, + 'size' => FALSE, + 'date' => FALSE, // File creation date + 'desc' => FALSE, // One line description of file + 'ldesc' => FALSE, // Can have multiple + 'created' => FALSE, + 'magic' => FALSE, + 'replaces' => FALSE, // ? and * are wildcards, as per DOS + 'CRC' => TRUE, // crc-32 + 'PATH' => TRUE, // can have multiple: [FTN] [unix timestamp] [datetime human readable] [signature] + 'SEENBY' => TRUE, + 'pw' => FALSE, // Password + ]; + + private File $file; + private Filearea $area; + private ?string $areadesc = NULL; + private ?string $pw = NULL; + + private Address $origin; // Should be first address in Path + private Address $from; // Should be last address in Path + private Address $to; // Should be me + + public function __construct(private string $filename) { + $fo = new File; + + $fo->kludges = collect(); + $fo->set_path = collect(); + $fo->set_seenby = collect(); + $fo->rogue_path = collect(); + $fo->rogue_seenby = collect(); + + list($hex,$name) = explode('-',$filename); + $ticfullpath = $this->fullpath($filename); + + if (! file_exists($ticfullpath)) + throw new FileNotFoundException(sprintf('%s:File [%s] doesnt exist',self::LOGKEY,realpath($ticfullpath))); + + if (! is_writable($ticfullpath)) + throw new UnableToWriteFile(sprintf('%s:File [%s] is not writable',self::LOGKEY,realpath($ticfullpath))); + + Log::info(sprintf('Processing TIC file [%s]',$ticfullpath)); + + $f = fopen($ticfullpath,'rb'); + if (! $f) { + Log::error(sprintf('%s:! Unable to open file [%s] for writing',self::LOGKEY,$ticfullpath)); + return; + } + + while (! feof($f)) { + $line = chop(fgets($f)); + $matches = []; + + if (! $line) + continue; + + preg_match('/([a-zA-Z]+)\ (.*)/',$line,$matches); + + if (in_array(strtolower($matches[1]),$this->_kludge)) { + switch ($k=strtolower($matches[1])) { + case 'area': + $this->{$k} = Filearea::singleOrNew(['name'=>strtoupper($matches[2])]); + break; + + case 'origin': + case 'from': + case 'to': + $this->{$k} = Address::findFTN($matches[2]); + + // @todo If $this->{$k} is null, we have discovered the system and it should be created + break; + + case 'file': + if (! file_exists($x=$this->fullpath(sprintf('%s-%s',$hex,$matches[2])))) + throw new FileNotFoundException(sprintf('File not found? [%s]',$x)); + + $fo->{$k} = $matches[2]; + $fo->fullname = $x; + break; + + case 'areadesc': + case 'pw': + case 'created': + $this->{$k} = $matches[2]; + break; + + case 'lfile': + case 'size': + case 'desc': + case 'magic': + case 'replaces': + $fo->{$k} = $matches[2]; + break; + + case 'fullname': + $fo->lfile = $matches[2]; + break; + + case 'date': + $fo->datetime = Carbon::create($matches[2]); + break; + + case 'ldesc': + $fo->{$k} .= $matches[2]; + break; + + case 'crc': + $fo->{$k} = hexdec($matches[2]); + break; + + case 'path': + $x = []; + preg_match(sprintf('#^[Pp]ath (%s)\ ?([0-9]+)\ ?(.*)$#',Address::ftn_regex),$line,$x); + $ao = Address::findFTN($x[1]); + + if (! $ao) { + $fo->rogue_path->push($matches[2]); + } else { + $fo->set_path->push(['address'=>$ao,'datetime'=>Carbon::createFromTimestamp($x[8]),'extra'=>$x[9]]); + } + + break; + + case 'seenby': + $ao = Address::findFTN($matches[2]); + + if (! $ao) { + $fo->rogue_seenby->push($matches[2]); + } else { + $fo->set_seenby->push($ao->id); + } + + break; + } + + } else { + $fo->kludges->push($line); + } + } + + fclose($f); + + $f = fopen($fo->fullname,'rb'); + $stat = fstat($f); + fclose($f); + + // Validate Size + if ($fo->size !== ($y=$stat['size'])) + throw new \Exception(sprintf('TIC file size [%d] doesnt match file [%s] (%d)',$fo->size,$fo->fullname,$y)); + + // Validate CRC + if (sprintf('%x',$fo->crc) !== ($y=hash_file('crc32b',$fo->fullname))) + throw new \Exception(sprintf('TIC file CRC [%x] doesnt match file [%s] (%s)',$fo->crc,$fo->fullname,$y)); + + // Validate Password + if ($this->pw !== ($y=$this->from->session('ticpass'))) + throw new \Exception(sprintf('TIC file PASSWORD [%s] doesnt match system [%s] (%s)',$this->pw,$this->from->ftn,$y)); + + // Validate Sender is linked (and permitted to send) + if ($this->from->fileareas->search(function($item) { return $item->id === $this->area->id; }) === FALSE) + throw new \Exception(sprintf('Node [%s] is not subscribed to [%s]',$this->from->ftn,$this->area->name)); + + // If the filearea is to be autocreated, create it + if (! $this->area->exists) { + $this->area->description = $this->areadesc; + $this->area->active = TRUE; + $this->area->public = FALSE; + $this->area->notes = 'Autocreated'; + $this->area->domain_id = $this->from->zone->domain_id; + $this->area->save(); + } + + $fo->filearea_id = $this->area->id; + $fo->fftn_id = $this->origin->id; + + // If the file create time is blank, we'll take the files + if (! $fo->datetime) + $fo->datetime = Carbon::createFromTimestamp($stat['ctime']); + + $fo->save(); + + $this->fo = $fo; + } + + public function fullpath(string $file,string $prefix=NULL): string + { + return sprintf('storage/app/%s/%s',config('app.fido'),($prefix ? $prefix.'-' : '').$file); + } +} \ No newline at end of file diff --git a/app/Classes/File/Item.php b/app/Classes/File/Item.php index 591e723..c12fd1b 100644 --- a/app/Classes/File/Item.php +++ b/app/Classes/File/Item.php @@ -4,7 +4,7 @@ namespace App\Classes\File; use Exception; use Illuminate\Contracts\Filesystem\FileNotFoundException; -use League\Flysystem\UnreadableFileException; +use League\Flysystem\UnreadableFileEncountered; /** * A file we are sending or receiving @@ -22,6 +22,7 @@ class Item protected const IS_FILE = (1<<3); protected const IS_FLO = (1<<4); protected const IS_REQ = (1<<5); + protected const IS_TIC = (1<<6); protected const I_RECV = (1<<6); protected const I_SEND = (1<<7); @@ -38,7 +39,7 @@ class Item /** * @throws FileNotFoundException - * @throws UnreadableFileException + * @throws UnreadableFileEncountered * @throws Exception */ public function __construct($file,int $action) @@ -54,7 +55,7 @@ class Item throw new FileNotFoundException('Item doesnt exist: '.$file); if (! is_readable($file)) - throw new UnreadableFileException('Item cannot be read: '.$file); + throw new UnreadableFileEncountered('Item cannot be read: '.$file); $this->file_name = $file; $x = stat($file); @@ -130,6 +131,9 @@ class Item if (strcasecmp(substr($x,1),'req') == 0) return self::IS_REQ; + if (strcasecmp(substr($x,1),'tic') == 0) + return self::IS_TIC; + for ($i=0;$ifile)); + + // Queue the tic to be processed later, in case the referenced file hasnt been received yet + TicProcess::dispatch($this->file); + + break; + default: Log::debug(sprintf('%s: - Leaving file [%s] in the inbound dir',self::LOGKEY,$this->file)); } diff --git a/app/Classes/File/Send.php b/app/Classes/File/Send.php index 0909d09..506872c 100644 --- a/app/Classes/File/Send.php +++ b/app/Classes/File/Send.php @@ -6,7 +6,7 @@ use Exception; use Illuminate\Contracts\Filesystem\FileNotFoundException; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use League\Flysystem\UnreadableFileException; +use League\Flysystem\UnreadableFileEncountered; use App\Models\Address; @@ -135,7 +135,7 @@ final class Send extends Item Log::error(sprintf('%s:! Item [%s] doesnt exist',self::LOGKEY,$file)); return; - } catch (UnreadableFileException) { + } catch (UnreadableFileEncountered) { Log::error(sprintf('%s:! Item [%s] cannot be read',self::LOGKEY,$file)); return; @@ -257,7 +257,7 @@ final class Send extends Item * * @param int $length * @return string|null - * @throws UnreadableFileException + * @throws UnreadableFileEncountered * @throws Exception */ public function read(int $length): ?string @@ -276,7 +276,7 @@ final class Send extends Item Log::debug(sprintf('%s: - Read [%d] bytes, file pos now [%d]',self::LOGKEY,strlen($data),$this->file_pos)); if ($data === FALSE) - throw new UnreadableFileException('Error reading file: '.$this->sending->file_name); + throw new UnreadableFileEncountered('Error reading file: '.$this->sending->file_name); return $data; } diff --git a/app/Classes/Protocol/Binkp.php b/app/Classes/Protocol/Binkp.php index f48cebd..acca48a 100644 --- a/app/Classes/Protocol/Binkp.php +++ b/app/Classes/Protocol/Binkp.php @@ -7,7 +7,7 @@ use Exception; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use League\Flysystem\UnreadableFileException; +use League\Flysystem\UnreadableFileEncountered; use App\Classes\Protocol as BaseProtocol; use App\Classes\Sock\SocketClient; @@ -511,7 +511,7 @@ final class Binkp extends BaseProtocol try { $data = $this->send->read(self::BP_BLKSIZE); - } catch (UnreadableFileException) { + } catch (UnreadableFileEncountered) { $this->send->close(FALSE); $this->sessionClear(self::SE_SENDFILE); diff --git a/app/Classes/Protocol/Zmodem.php b/app/Classes/Protocol/Zmodem.php index b216b99..fe51c64 100644 --- a/app/Classes/Protocol/Zmodem.php +++ b/app/Classes/Protocol/Zmodem.php @@ -3,7 +3,6 @@ namespace App\Classes\Protocol; use Illuminate\Support\Facades\Log; -use League\Flysystem\UnreadableFileException; use App\Classes\Protocol; use App\Classes\Protocol\Zmodem as ZmodemClass; diff --git a/app/Console/Commands/TicProcess.php b/app/Console/Commands/TicProcess.php new file mode 100644 index 0000000..91583d4 --- /dev/null +++ b/app/Console/Commands/TicProcess.php @@ -0,0 +1,37 @@ +argument('file')); + + return Command::SUCCESS; + } +} diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php index d565944..2ece69a 100644 --- a/app/Http/Controllers/SystemController.php +++ b/app/Http/Controllers/SystemController.php @@ -12,7 +12,7 @@ use Illuminate\Support\Facades\Notification; use Illuminate\Support\ViewErrorBag; use App\Http\Requests\SystemRegister; -use App\Models\{Address,Echoarea,Setup,System,SystemZone,Zone}; +use App\Models\{Address,Echoarea,Filearea,Setup,System,SystemZone,Zone}; use App\Notifications\AddressLink; use App\Rules\{FidoInteger,TwoByteInteger}; @@ -410,6 +410,40 @@ class SystemController extends Controller ->with('echoareas',$eo); } + /** + * Update the systems fileareas + * + * @param Request $request + * @param System $o + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse + */ + public function fileareas(Request $request,System $o) + { + $ao = $o->addresses->firstWhere('id',$request->address_id); + + if (($request->method() == 'POST') && $request->post()) { + session()->flash('accordion','filearea'); + + // Ensure we have session details for this address. + if (! $ao->session('sespass')) + return redirect()->back()->withErrors('System doesnt belong to this network'); + + $ao->fileareas()->syncWithPivotValues($request->get('id',[]),['subscribed'=>Carbon::now()]); + + return redirect()->back()->with('success','Fileareas updated');; + } + + $fo = Filearea::active() + ->where('domain_id',$ao->zone->domain_id) + ->orderBy('name') + ->get(); + + return view('system.widget.filearea') + ->with('o',$o) + ->with('ao',$ao) + ->with('fileareas',$fo); + } + public function home() { return view('system.home'); diff --git a/app/Jobs/TicProcess.php b/app/Jobs/TicProcess.php new file mode 100644 index 0000000..126cf6c --- /dev/null +++ b/app/Jobs/TicProcess.php @@ -0,0 +1,40 @@ +file); + } +} \ No newline at end of file diff --git a/app/Models/Address.php b/app/Models/Address.php index 960d083..fb00c56 100644 --- a/app/Models/Address.php +++ b/app/Models/Address.php @@ -159,7 +159,29 @@ class Address extends Model public function echomails() { return $this->belongsToMany(Echomail::class,'echomail_seenby') - ->withPivot(['sent_at','packet']); + ->withPivot(['sent_at','export_at','packet']); + } + + /** + * Files that this address has seen + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function files() + { + return $this->belongsToMany(File::class,'file_seenby') + ->withPivot(['sent_at','export_at']); + } + + /** + * Echoareas this address is subscribed to + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function fileareas() + { + return $this->belongsToMany(Filearea::class) + ->withPivot(['subscribed']); } /** @@ -409,7 +431,7 @@ class Address extends Model } /** - * Netmail waiting to be sent to this system + * Echomail waiting to be sent to this system * * @return Collection */ @@ -421,6 +443,19 @@ class Address extends Model ->get(); } + /** + * Files waiting to be sent to this system + * + * @return Collection + */ + public function filesWaiting(): Collection + { + return $this->files() + ->whereNull('file_seenby.sent_at') + ->whereNotNull('file_seenby.export_at') + ->get(); + } + /** * Get echomail for this node * diff --git a/app/Models/Echomail.php b/app/Models/Echomail.php index 8d5c0c0..2a70788 100644 --- a/app/Models/Echomail.php +++ b/app/Models/Echomail.php @@ -46,7 +46,7 @@ final class Echomail extends Model implements Packet protected $dates = ['datetime']; - public function __set($key, $value) + public function __set($key,$value) { switch ($key) { case 'no_export': @@ -68,7 +68,7 @@ final class Echomail extends Model implements Packet // @todo if the message is updated with new SEEN-BY's from another route, we'll delete the pending export for systems (if there is one) static::created(function($model) { if (! $model->echoarea_id) { - Log::alert(sprintf('%s:- Message has no echo area, not exporting',self::LOGKEY,$model->id)); + Log::alert(sprintf('%s:- Message has no echoarea, not exporting',self::LOGKEY,$model->id)); return; } diff --git a/app/Models/File.php b/app/Models/File.php new file mode 100644 index 0000000..638e39d --- /dev/null +++ b/app/Models/File.php @@ -0,0 +1,157 @@ + CollectionOrNull::class, + 'rogue_seenby' => CollectionOrNull::class, + 'rogue_path' => CollectionOrNull::class, + 'desc' => CompressedString::class, + 'ldesc' => CompressedString::class, + 'size' => 'int', + ]; + + private const cast_utf8 = [ + 'desc', + 'ldesc', + ]; + + protected $dates = ['datetime']; + + public function __set($key,$value) + { + switch ($key) { + case 'fullname': + case 'replaces': + case 'no_export': + case 'set_path': + case 'set_packet': + case 'set_seenby': + $this->{$key} = $value; + break; + + default: + parent::__set($key,$value); + } + } + + public static function boot() + { + parent::boot(); + + static::creating(function($model) { + Log::debug(sprintf('%s:- Storing file [%s]',self::LOGKEY,$model->file)); + + // Store file + + // Delete file from inbound + + // Delete anything being replaced + }); + + // @todo if the file is updated with new SEEN-BY's from another route, we'll delete the pending export for systems (if there is one) + static::created(function($model) { + if (! $model->filearea_id) { + Log::alert(sprintf('%s:- File has no filearea, not exporting',self::LOGKEY,$model->id)); + return; + } + + $so = Setup::findOrFail(config('app.id')); + + // Our address + $ftns = $so + ->system + ->match($model->fftn->zone,Address::NODE_ACTIVE|Address::NODE_PVT|Address::NODE_HOLD); + + // Add our address to the seenby; + $model->set_seenby = $model->set_seenby->merge($ftns->pluck('id'))->unique(); + $model->set_path = $model->set_path->merge([[ + 'address'=>$ftns->first(), + 'datetime'=>($x=Carbon::now())->timestamp, + 'extra'=>sprintf('%s %s (%s)',$x->toRfc7231String(),$so::PRODUCT_NAME,$so->version), + ]]); + + // Save the seenby + $model->seenby()->sync($model->set_seenby); + + // Save the Path + $ppoid = NULL; + foreach ($model->set_path as $path) { + $po = DB::select('INSERT INTO file_path (file_id,address_id,parent_id,datetime,extra) VALUES (?,?,?,?,?) RETURNING id',[ + $model->id, + $path['address']->id, + $ppoid, + Carbon::createFromTimestamp($path['datetime']), + $path['extra'], + ]); + + $ppoid = $po[0]->id; + } + + // See if we need to export this message. + $exportto = $model->filearea->addresses->pluck('id')->diff($model->set_seenby); + + if ($exportto->count()) { + if ($model->no_export) { + Log::debug(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); + return; + } + + Log::debug(sprintf('%s:- Exporting file [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(','))); + + // Save the seenby for the exported systems + $model->seenby()->syncWithPivotValues($exportto,['export_at'=>Carbon::now()],FALSE); + } + }); + } + + /* RELATIONS */ + + public function filearea() + { + return $this->belongsTo(Filearea::class); + } + + public function fftn() + { + return $this->belongsTo(Address::class) + ->withTrashed(); + } + + public function seenby() + { + return $this->belongsToMany(Address::class,'file_seenby') + ->ftnOrder(); + } + + public function path() + { + return $this->belongsToMany(Address::class,'file_path') + ->withPivot(['id','parent_id','extra']); + } + + /* METHODS */ + + public function jsonSerialize(): array + { + return $this->encode(); + } +} diff --git a/app/Models/Filearea.php b/app/Models/Filearea.php index d96437a..1f1edf6 100644 --- a/app/Models/Filearea.php +++ b/app/Models/Filearea.php @@ -11,6 +11,10 @@ class Filearea extends Model { use SoftDeletes,ScopeActive; + protected $fillable = [ + 'name', + ]; + /* RELATIONS */ public function addresses() diff --git a/app/Models/System.php b/app/Models/System.php index afc3a8c..f27d163 100644 --- a/app/Models/System.php +++ b/app/Models/System.php @@ -92,6 +92,14 @@ class System extends Model ->where('addresses.system_id',$this->id); } + public function fileareas() + { + return Filearea::select('fileareas.*') + ->join('address_filearea',['address_filearea.filearea_id'=>'fileareas.id']) + ->join('addresses',['addresses.id'=>'address_filearea.address_id']) + ->where('addresses.system_id',$this->id); + } + /** * Return the system name, or role name for the zone * diff --git a/database/migrations/2022_10_31_122701_files.php b/database/migrations/2022_10_31_122701_files.php new file mode 100644 index 0000000..0797243 --- /dev/null +++ b/database/migrations/2022_10_31_122701_files.php @@ -0,0 +1,85 @@ +id(); + $table->timestamps(); + $table->softDeletes(); + + $table->string('file'); + $table->string('magic')->nullable(); + $table->integer('size'); + $table->bigInteger('crc'); + $table->datetime('datetime'); + $table->string('lfile')->nullable(); + $table->string('desc')->nullable(); + $table->text('ldesc')->nullable(); + + $table->jsonb('rogue_path')->nullable(); + $table->jsonb('rogue_seenby')->nullable(); + $table->jsonb('kludges')->nullable(); + + $table->bigInteger('filearea_id'); + $table->foreign('filearea_id')->references('id')->on('fileareas'); + + $table->bigInteger('fftn_id'); + $table->foreign('fftn_id')->references('id')->on('addresses'); + }); + + Schema::create('file_seenby', function (Blueprint $table) { + $table->bigInteger('address_id'); + $table->foreign('address_id')->references('id')->on('addresses'); + + $table->bigInteger('file_id'); + $table->foreign('file_id')->references('id')->on('files'); + + $table->datetime('export_at')->nullable(); + $table->datetime('sent_at')->nullable(); + + $table->unique(['address_id','file_id']); + }); + + Schema::create('file_path', function (Blueprint $table) { + $table->id(); + + $table->bigInteger('address_id'); + $table->foreign('address_id')->references('id')->on('addresses'); + + $table->unique(['id','file_id']); + $table->unique(['file_id','address_id','parent_id']); + + $table->bigInteger('parent_id')->nullable(); + $table->foreign(['parent_id','file_id'])->references(['id','file_id'])->on('file_path'); + + $table->bigInteger('file_id'); + $table->foreign('file_id')->references('id')->on('files'); + + $table->datetime('datetime'); + $table->string('extra')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('file_path'); + Schema::dropIfExists('file_seenby'); + Schema::dropIfExists('files'); + } +}; diff --git a/resources/views/system/addedit.blade.php b/resources/views/system/addedit.blade.php index ec64afb..5fdb39d 100644 --- a/resources/views/system/addedit.blade.php +++ b/resources/views/system/addedit.blade.php @@ -1,13 +1,10 @@ + @extends('layouts.app') @section('htmlheader_title') @can('admin',$o) @if($o->exists) Update @else Add @endif @endcan System @endsection -@php -use App\Models\Address as Address; -@endphp - @section('content') @if($o->exists)

{{ $o->name }}@if($o->setup)*@endif

@@ -175,7 +172,7 @@ use App\Models\Address as Address; @if(($x=$oo->systems->where('pivot.default',TRUE))->count() && ($x->first()->id !== $o->id)) @else - + @endif {{ $oo->pivot->sespass }} @@ -213,6 +210,18 @@ use App\Models\Address as Address; + +
+ + +
+
+

This system can subscribe to the following fileareas:

+ @include('system.form-filearea') +
+
+
+
@@ -289,15 +298,15 @@ use App\Models\Address as Address;
- +
- +
-
+
Netmails are waiting for these addresses: @@ -319,7 +328,7 @@ use App\Models\Address as Address; -
+
Echomail waiting for these addresses:
@@ -339,6 +348,28 @@ use App\Models\Address as Address;
+ + +
+ Files waiting for these addresses: + + + + + + + + + + @foreach ($o->addresses->sortBy('zone.zone_id') as $ao) + + + + + @endforeach + +
AddressFiles
{{ $ao->ftn3d }}{{ $ao->filesWaiting()->count() }}
+
diff --git a/resources/views/system/form-echoarea.blade.php b/resources/views/system/form-echoarea.blade.php index 1d66ead..3bd73b4 100644 --- a/resources/views/system/form-echoarea.blade.php +++ b/resources/views/system/form-echoarea.blade.php @@ -13,10 +13,10 @@
- +
- @foreach($x as $do) @@ -32,11 +32,11 @@
-
- +
+
-
@@ -79,13 +79,13 @@ + + +@append \ No newline at end of file diff --git a/resources/views/system/widget/filearea.blade.php b/resources/views/system/widget/filearea.blade.php new file mode 100644 index 0000000..da0fe1f --- /dev/null +++ b/resources/views/system/widget/filearea.blade.php @@ -0,0 +1,44 @@ +
+
+ + + + + + + + + + + @foreach ($fileareas as $oo) + + + + + + @endforeach + +
SubscribedFileareaDescription
fileareas->search(function($item) use ($oo) { return $item->id == $oo->id; }) !== FALSE)checked @endif>{{ $oo->name }}{{ $oo->description }}
+
+
+ +
+
+ Cancel +
+ + + @if($errors->count()) + + There were errors with the submission. + @dump($errors) + + @endif + + + @can('admin',$o) +
+ +
+ @endcan +
\ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 86de305..e982a48 100644 --- a/routes/web.php +++ b/routes/web.php @@ -73,6 +73,8 @@ Route::middleware(['auth','verified','activeuser'])->group(function () { ->where('zo','[0-9]+'); Route::match(['get','post'],'ftn/system/echoarea/{o}',[SystemController::class,'echoareas']) ->where('o','[0-9]+'); + Route::match(['get','post'],'ftn/system/filearea/{o}',[SystemController::class,'fileareas']) + ->where('o','[0-9]+'); Route::match(['get','post'],'ftn/system/movaddress/{so}/{o}',[SystemController::class,'mov_address']) ->where('so','[0-9]+') ->where('o','[0-9]+');