225 lines
6.0 KiB
PHP
225 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Carbon\Carbon;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
use App\Models\Casts\{CompressedStringOrNull,CollectionOrNull};
|
|
|
|
class File extends Model
|
|
{
|
|
use SoftDeletes;
|
|
|
|
private const LOGKEY = 'MF-';
|
|
private bool $no_export = FALSE;
|
|
|
|
public Collection $set_path;
|
|
public Collection $set_seenby;
|
|
public string $src_file = '';
|
|
|
|
protected $casts = [
|
|
'kludges' => CollectionOrNull::class,
|
|
'datetime' => 'datetime:Y-m-d H:i:s',
|
|
'desc' => CompressedStringOrNull::class,
|
|
'ldesc' => CompressedStringOrNull::class,
|
|
'rogue_seenby' => CollectionOrNull::class,
|
|
'rogue_path' => CollectionOrNull::class,
|
|
'size' => 'int',
|
|
];
|
|
|
|
public static function boot()
|
|
{
|
|
parent::boot();
|
|
|
|
static::creating(function($model) {
|
|
if (! $model->filearea_id) {
|
|
Log::alert(sprintf('%s:- File has no filearea, not processing creating [%s]',self::LOGKEY,$model->name));
|
|
return;
|
|
}
|
|
|
|
Log::info(sprintf('%s:- Storing file [%s] in [%s]',self::LOGKEY,$model->src_file,$model->rel_name));
|
|
|
|
$srcfs = Storage::disk(config('fido.local_disk'));
|
|
$tgtfs = Storage::disk(config('fido.file_disk'));
|
|
|
|
// Delete anything being replaced
|
|
foreach (self::where('name',$model->name)->where('filearea_id',$model->filearea_id)->get() as $fo) {
|
|
Log::info(sprintf('%s:%% Deleting old file record [%d] for file [%s]',self::LOGKEY,$fo->id,$fo->rel_name));
|
|
|
|
$tgtfs->move($fo->rel_name,$fo->relname.'.'.$fo->id);
|
|
$fo->delete();
|
|
}
|
|
|
|
// Store file
|
|
if ($tgtfs->put($model->rel_name,$srcfs->get($model->src_file),'public')) {
|
|
$srcfs->delete($model->src_file);
|
|
|
|
} else {
|
|
throw new \Exception(sprintf('Unable to move file [%s] to [%s]',$model->src_file,$model->rel_name));
|
|
}
|
|
});
|
|
|
|
// @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 [%d]',self::LOGKEY,$model->id));
|
|
return;
|
|
}
|
|
|
|
$rogue = collect();
|
|
$seenby = collect();
|
|
$path = collect();
|
|
|
|
$zone = $model->fftn->zone;
|
|
|
|
// Parse PATH
|
|
/**
|
|
* Path 21:4/106.0 @231005001126 PST+7 Foobar
|
|
* Path 21:1/100 1696489954 Thu Oct 05 07:12:34 2023 UTC htick/lnx 1.9 2022-07-03
|
|
*/
|
|
foreach ($model->set_path as $line) {
|
|
$matches = [];
|
|
preg_match(sprintf('#^(%s)\ ((@?)(\d+)(\ ([A-Z]{3}([\+\-][0-9]+)))?)\ ?(.*)$#',Address::ftn_regex),$line,$matches);
|
|
|
|
// If our domain is blank, get the model's domain
|
|
if (! Arr::get($matches,6))
|
|
$matches[1] .= '@'.$model->filearea->domain->name;
|
|
|
|
if ($x=Arr::get($matches,1)) {
|
|
$ftn = Address::parseFTN($x);
|
|
|
|
// If domain should be flattened, look for node regardless of zone (within the list of zones for the domain)
|
|
$ao = ($zone->domain->flatten)
|
|
? Address::findZone($zone->domain,$ftn['n'],$ftn['f'],0)
|
|
: Address::findFTN($x);
|
|
|
|
if (! $ao)
|
|
$ao = Address::createFTN($x,System::createUnknownSystem());
|
|
|
|
$datetime = ($matches[8] === '@')
|
|
? Carbon::createFromFormat('ymdHis',$matches[9],$matches[12])
|
|
: Carbon::createFromTimestamp($matches[7]);
|
|
|
|
$path->push(['address'=>$ao,'datetime'=>$datetime,'extra'=>$matches[13]]);
|
|
}
|
|
}
|
|
|
|
// Save the Path
|
|
$ppoid = NULL;
|
|
foreach ($path as $item) {
|
|
$po = DB::select('INSERT INTO file_path (file_id,address_id,parent_id,datetime,extra) VALUES (?,?,?,?,?) RETURNING id',[
|
|
$model->id,
|
|
$item['address']->id,
|
|
$ppoid,
|
|
$item['datetime'],
|
|
$item['extra'],
|
|
]);
|
|
|
|
$ppoid = $po[0]->id;
|
|
}
|
|
|
|
// Make sure all the path is in the seenby
|
|
// Add zone to seenby
|
|
$model->set_seenby = $model->set_seenby->merge($path->pluck('address.ftn3d'))->unique()->filter();
|
|
|
|
foreach ($model->set_seenby as $sb) {
|
|
if (! preg_match('/@([a-zA-Z0-9\-_~]{0,8})/',$sb))
|
|
$sb .= '@'.$model->filearea->domain->name;
|
|
|
|
$ftn = Address::parseFTN($sb);
|
|
|
|
$ao = ($zone->domain->flatten)
|
|
? Address::findZone($zone->domain,$ftn['n'],$ftn['f'],0)
|
|
: Address::findFTN($sb);
|
|
|
|
if ($ao)
|
|
$seenby->push($ao->id);
|
|
else
|
|
$rogue->push($sb);
|
|
}
|
|
|
|
$model->rogue_seenby = $rogue;
|
|
|
|
$model->seenby()->sync($seenby);
|
|
$model->save();
|
|
|
|
// See if we need to export this file.
|
|
if ($model->filearea->sec_read) {
|
|
$exportto = $model
|
|
->filearea
|
|
->addresses
|
|
->filter(function($item) use ($model) { return $model->filearea->can_read($item->security); })
|
|
->pluck('id')
|
|
->diff($seenby);
|
|
|
|
if ($exportto->count()) {
|
|
if ($model->no_export) {
|
|
Log::alert(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(',')));
|
|
return;
|
|
}
|
|
|
|
Log::info(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','datetime','extra']);
|
|
}
|
|
|
|
/* ATTRIBUTES */
|
|
|
|
public function getOriginAttribute(): Address
|
|
{
|
|
return $this->path->where('pivot.parent_id','=',NULL)->pop();
|
|
}
|
|
|
|
/**
|
|
* Return the relative path to Storage::disk() in the store
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getRelNameAttribute(): string
|
|
{
|
|
return sprintf('%04X/%s',$this->filearea_id,$this->name);
|
|
}
|
|
|
|
/* METHODS */
|
|
|
|
public function jsonSerialize(): array
|
|
{
|
|
return $this->encode();
|
|
}
|
|
} |