protected function copyAsset($from=null, $to=null, $write=false)
    {
        if (!is_string($from) or !is_string($to)) {
            return false;
        }
        $log = [
            'type'   => 'asset',
            'status' => '',
            'reason' => '',
            // Use unnormalized, relative paths in log, because they
            // might help understand why a file was ignored
            'source' => $from,
            'dest'   => 'static/',
            'size'   => null
        ];

        // Source can be absolute
        if ($this->isAbsolutePath($from)) {
            $source = $from;
        } else {
            $source = $this->normalizePath($this->root . '/' . $from);
        }

        // But target is always relative to static dir
        $target = $this->normalizePath($this->outputdir . '/' . $to);
        if ($this->filterPath($target) == false) {
            $log['status'] = 'ignore';
            $log['reason'] = 'Cannot copy asset outside of the static folder';
            return $this->summary[] = $log;
        }
        $log['dest'] .= str_replace($this->outputdir . '/', '', $target);

        // Get type of asset
        if (is_dir($source)) {
            $log['type'] = 'dir';
        }
        elseif (is_file($source)) {
            $log['type'] = 'file';
        }
        else {
            $log['status'] = 'ignore';
            $log['reason'] = 'Source file or folder not found';
        }

        // Copy a folder
        if ($write && $log['type'] == 'dir') {
            $source = new Folder($source);
            $existing = new Folder($target);
            if ($existing->exists()) $existing->remove();
            $log['status'] = $source->copy($target) ? 'done' : 'failed';
        }

        // Copy a file
        if ($write && $log['type'] == 'file') {
            $log['status'] = copy($source, $target) ? 'done' : 'failed';
        }

        return $this->summary[] = $log;
    }