<commit_before><?php

declare(strict_types=1);

namespace atk4\ui\demo;

try {
    if (file_exists(__DIR__ . '/db.php')) {
        require_once __DIR__ . '/db.php';
    } else {
        require_once __DIR__ . '/db.default.php';
    }
} catch (PDOException $e) {
    // do not pass $e unless you can secure DSN!
    throw (new \atk4\ui\Exception('This demo requires access to the database. See "demos/init-db.php"'))
        ->addMoreInfo('PDO error', $e->getMessage());
}

// a very basic file that sets up Agile Data to be used in some demonstrations

trait ModelLockTrait
{
    public function lock(): void
    {
        $this->getAction('add')->callback = function ($m) {
            return 'Form Submit! Data are not save in demo mode.';
        };
        $this->getAction('edit')->callback = function ($m) {
            return 'Form Submit! Data are not save in demo mode.';
        };

        $delete = $this->getAction('delete');
        $delete->confirmation = 'Please go ahead. Demo mode does not really delete data.';

        $delete->callback = function ($m) {
            return 'Only simulating delete when in demo mode.';
        };
    }
}

class Country extends \atk4\data\Model
{
    public $table = 'country';

    public function init(): void
    {
        parent::init();
        $this->addField('name', ['actual' => 'nicename', 'required' => true, 'type' => 'string']);
        $this->addField('sys_name', ['actual' => 'name', 'system' => true]);

        $this->addField('iso', ['caption' => 'ISO', 'required' => true, 'type' => 'string', 'ui' => ['table' => ['sortable' => false]]]);
        $this->addField('iso3', ['caption' => 'ISO3', 'required' => true, 'type' => 'string']);
        $this->addField('numcode', ['caption' => 'ISO Numeric Code', 'type' => 'number', 'required' => true]);
        $this->addField('phonecode', ['caption' => 'Phone Prefix', 'type' => 'number', 'required' => true]);

        $this->onHook(\atk4\data\Model::HOOK_BEFORE_SAVE, function (\atk4\data\Model $m) {
            if (!$m->get('sys_name')) {
                $m->set('sys_name', mb_strtoupper($m->get('name')));
            }
        });
    }

    public function validate($intent = null)
    {
        $errors = parent::validate($intent);

        if (mb_strlen($this['iso']) !== 2) {
            $errors['iso'] = 'Must be exactly 2 characters';
        }

        if (mb_strlen($this['iso3']) !== 3) {
            $errors['iso3'] = 'Must be exactly 3 characters';
        }

        // look if name is unique
        $c = clone $this;
        $c->unload();
        $c->tryLoadBy('name', $this['name']);
        if ($c->loaded() && $c->id !== $this->id) {
            $errors['name'] = 'Country name must be unique';
        }

        return $errors;
    }
}

class CountryLock extends Country
{
    use ModelLockTrait;
    public $caption = 'Country';

    public function init(): void
    {
        parent::init();
        $this->lock();
    }
}

class Stat extends \atk4\data\Model
{
    public $table = 'stats';
    public $title = 'Project Stat';

    public function init(): void
    {
        parent::init();

        $this->addFields(['project_name', 'project_code'], ['type' => 'string']);
        $this->title_field = 'project_name';
        $this->addField('description', ['type' => 'text']);
        $this->addField('client_name', ['type' => 'string']);
        $this->addField('client_address', ['type' => 'text', 'ui' => ['form' => [new \atk4\ui\FormField\TextArea(), 'rows' => 4]]]);

        $this->hasOne('client_country_iso', [
            new Country(),
            'their_field' => 'iso',
            'ui' => [
                'form' => \atk4\ui\FormField\Line::class,
            ],
        ])
            ->addField('client_country', 'name');

        $this->addField('is_commercial', ['type' => 'boolean']);
        $this->addField('currency', ['enum' => ['EUR', 'USD', 'GBP']]);
        $this->addField('currency_symbol', ['never_persist' => true]);
        $this->onHook(\atk4\data\Model::HOOK_AFTER_LOAD, function (\atk4\data\Model $m) {
            /* implementation for "intl"
            $locale='en-UK';
            $fmt = new \NumberFormatter( $locale."@currency=".$m->get('currency'), NumberFormatter::CURRENCY );
            $m->set('currency_symbol', $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL));
             */

            $map = ['EUR' => '€', 'USD' => '$', 'GBP' => '£'];
            $m->set('currency_symbol', $map[$m->get('currency')] ?? '?');
        });

        $this->addFields(['project_budget', 'project_invoiced', 'project_paid', 'project_hour_cost'], ['type' => 'money']);

        $this->addFields(['project_hours_est', 'project_hours_reported'], ['type' => 'integer']);

        $this->addFields(['project_expenses_est', 'project_expenses'], ['type' => 'money']);
        $this->addField('project_mgmt_cost_pct', new Percent());
        $this->addField('project_qa_cost_pct', new Percent());

        $this->addFields(['start_date', 'finish_date'], ['type' => 'date']);
        $this->addField('finish_time', ['type' => 'time']);

        $this->addFields(['created', 'updated'], ['type' => 'datetime', 'ui' => ['form' => [\atk4\ui\FormField\Line::class, 'disabled' => true]]]);
    }
}

class Percent extends \atk4\data\Field
{
    public $type = 'float'; // will need to be able to affect rendering and storage
}

class File extends \atk4\data\Model
{
    public $table = 'file';

    public function init(): void
    {
        parent::init();
        $this->addField('name');

        $this->addField('type', ['caption' => 'MIME Type']);
        $this->addField('is_folder', ['type' => 'boolean']);

        $this->hasMany('SubFolder', [new self(), 'their_field' => 'parent_folder_id'])
            ->addField('count', ['aggregate' => 'count', 'field' => $this->expr('*')]);

        $this->hasOne('parent_folder_id', new self())
            ->addTitle();
    }

    /**
     * Perform import from filesystem.
     */
    public function importFromFilesystem($path, $isSub = false)
    {
        $dir = new \DirectoryIterator($path);
        foreach ($dir as $fileinfo) {
            $name = $fileinfo->getFilename();

            if ($fileinfo->getFilename() === '.') {
                continue;
            }
            if ($fileinfo->getFilename()[0] === '.') {
                continue;
            }

            if ($fileinfo->getFilename() === 'src' || $fileinfo->getFilename() === 'demos' || $isSub) {
                $this->unload();
                $this->save([
                    'name' => $fileinfo->getFilename(),
                    'is_folder' => $fileinfo->isDir(),
                    'type' => pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION),
                ]);

                if ($fileinfo->isDir()) {
                    $this->ref('SubFolder')->importFromFilesystem($path . '/' . $fileinfo->getFilename(), true);
                }
            }
        }
    }
}

class FileLock extends File
{
    use ModelLockTrait;
    public $caption = 'File';

    public function init(): void
    {
        parent::init();
        $this->lock();
    }
}<commit_msg>[fix] File Import Demo (#1299)<commit_after><?php

declare(strict_types=1);

namespace atk4\ui\demo;

try {
    if (file_exists(__DIR__ . '/db.php')) {
        require_once __DIR__ . '/db.php';
    } else {
        require_once __DIR__ . '/db.default.php';
    }
} catch (PDOException $e) {
    // do not pass $e unless you can secure DSN!
    throw (new \atk4\ui\Exception('This demo requires access to the database. See "demos/init-db.php"'))
        ->addMoreInfo('PDO error', $e->getMessage());
}

// a very basic file that sets up Agile Data to be used in some demonstrations

trait ModelLockTrait
{
    public function lock(): void
    {
        $this->getAction('add')->callback = function ($m) {
            return 'Form Submit! Data are not save in demo mode.';
        };
        $this->getAction('edit')->callback = function ($m) {
            return 'Form Submit! Data are not save in demo mode.';
        };

        $delete = $this->getAction('delete');
        $delete->confirmation = 'Please go ahead. Demo mode does not really delete data.';

        $delete->callback = function ($m) {
            return 'Only simulating delete when in demo mode.';
        };
    }
}

class Country extends \atk4\data\Model
{
    public $table = 'country';

    public function init(): void
    {
        parent::init();
        $this->addField('name', ['actual' => 'nicename', 'required' => true, 'type' => 'string']);
        $this->addField('sys_name', ['actual' => 'name', 'system' => true]);

        $this->addField('iso', ['caption' => 'ISO', 'required' => true, 'type' => 'string', 'ui' => ['table' => ['sortable' => false]]]);
        $this->addField('iso3', ['caption' => 'ISO3', 'required' => true, 'type' => 'string']);
        $this->addField('numcode', ['caption' => 'ISO Numeric Code', 'type' => 'number', 'required' => true]);
        $this->addField('phonecode', ['caption' => 'Phone Prefix', 'type' => 'number', 'required' => true]);

        $this->onHook(\atk4\data\Model::HOOK_BEFORE_SAVE, function (\atk4\data\Model $m) {
            if (!$m->get('sys_name')) {
                $m->set('sys_name', mb_strtoupper($m->get('name')));
            }
        });
    }

    public function validate($intent = null)
    {
        $errors = parent::validate($intent);

        if (mb_strlen($this['iso']) !== 2) {
            $errors['iso'] = 'Must be exactly 2 characters';
        }

        if (mb_strlen($this['iso3']) !== 3) {
            $errors['iso3'] = 'Must be exactly 3 characters';
        }

        // look if name is unique
        $c = clone $this;
        $c->unload();
        $c->tryLoadBy('name', $this['name']);
        if ($c->loaded() && $c->id !== $this->id) {
            $errors['name'] = 'Country name must be unique';
        }

        return $errors;
    }
}

class CountryLock extends Country
{
    use ModelLockTrait;
    public $caption = 'Country';

    public function init(): void
    {
        parent::init();
        $this->lock();
    }
}

class Stat extends \atk4\data\Model
{
    public $table = 'stats';
    public $title = 'Project Stat';

    public function init(): void
    {
        parent::init();

        $this->addFields(['project_name', 'project_code'], ['type' => 'string']);
        $this->title_field = 'project_name';
        $this->addField('description', ['type' => 'text']);
        $this->addField('client_name', ['type' => 'string']);
        $this->addField('client_address', ['type' => 'text', 'ui' => ['form' => [new \atk4\ui\FormField\TextArea(), 'rows' => 4]]]);

        $this->hasOne('client_country_iso', [
            new Country(),
            'their_field' => 'iso',
            'ui' => [
                'form' => \atk4\ui\FormField\Line::class,
            ],
        ])
            ->addField('client_country', 'name');

        $this->addField('is_commercial', ['type' => 'boolean']);
        $this->addField('currency', ['enum' => ['EUR', 'USD', 'GBP']]);
        $this->addField('currency_symbol', ['never_persist' => true]);
        $this->onHook(\atk4\data\Model::HOOK_AFTER_LOAD, function (\atk4\data\Model $m) {
            /* implementation for "intl"
            $locale='en-UK';
            $fmt = new \NumberFormatter( $locale."@currency=".$m->get('currency'), NumberFormatter::CURRENCY );
            $m->set('currency_symbol', $fmt->getSymbol(NumberFormatter::CURRENCY_SYMBOL));
             */

            $map = ['EUR' => '€', 'USD' => '$', 'GBP' => '£'];
            $m->set('currency_symbol', $map[$m->get('currency')] ?? '?');
        });

        $this->addFields(['project_budget', 'project_invoiced', 'project_paid', 'project_hour_cost'], ['type' => 'money']);

        $this->addFields(['project_hours_est', 'project_hours_reported'], ['type' => 'integer']);

        $this->addFields(['project_expenses_est', 'project_expenses'], ['type' => 'money']);
        $this->addField('project_mgmt_cost_pct', new Percent());
        $this->addField('project_qa_cost_pct', new Percent());

        $this->addFields(['start_date', 'finish_date'], ['type' => 'date']);
        $this->addField('finish_time', ['type' => 'time']);

        $this->addFields(['created', 'updated'], ['type' => 'datetime', 'ui' => ['form' => [\atk4\ui\FormField\Line::class, 'disabled' => true]]]);
    }
}

class Percent extends \atk4\data\Field
{
    public $type = 'float'; // will need to be able to affect rendering and storage
}

class File extends \atk4\data\Model
{
    public $table = 'file';

    public function init(): void
    {
        parent::init();
        $this->addField('name');

        $this->addField('type', ['caption' => 'MIME Type']);
        $this->addField('is_folder', ['type' => 'boolean']);

        $this->hasMany('SubFolder', [new self(), 'their_field' => 'parent_folder_id'])
            ->addField('count', ['aggregate' => 'count', 'field' => $this->expr('*')]);

        $this->hasOne('parent_folder_id', new self())
            ->addTitle();
    }

    /**
     * Perform import from filesystem.
     */
    public function importFromFilesystem($path, $isSub = false)
    {
        $dir = new \DirectoryIterator($path);
        foreach ($dir as $fileinfo) {
            $name = $fileinfo->getFilename();

            if ($fileinfo->getFilename() === '.') {
                continue;
            }
            if ($fileinfo->getFilename()[0] === '.') {
                continue;
            }

            if ($fileinfo->getFilename() === 'src' || $fileinfo->getFilename() === 'demos' || $isSub) {
                /* Disabling saving file in db
                $this->unload();
                $this->save([
                    'name' => $fileinfo->getFilename(),
                    'is_folder' => $fileinfo->isDir(),
                    'type' => pathinfo($fileinfo->getFilename(), PATHINFO_EXTENSION),
                ]);
                */

                if ($fileinfo->isDir()) {
                    $this->ref('SubFolder')->importFromFilesystem($path . '/' . $fileinfo->getFilename(), true);
                }
            }
        }
    }
}

class FileLock extends File
{
    use ModelLockTrait;
    public $caption = 'File';

    public function init(): void
    {
        parent::init();
        $this->lock();
    }
}
{'fraction_non_alphanumeric': 0.19930627818244884, 'fraction_numerical': 0.003607353451266042, 'mean_word_length': 2.3808630393996246, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 253, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1307724', 'n_tokens_mistral': 4484, 'n_tokens_neox': 4363, 'n_words': 945}