Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Update Manager Refactor #1259

Draft
wants to merge 66 commits into
base: wip/support-uploading-packages
Choose a base branch
from

Conversation

jaxwilko
Copy link
Member

@jaxwilko jaxwilko commented Nov 29, 2024

Me and Luke are working on a little refactor of how the UpdateManager works...

It also includes some components from my old composer PR found here: #967

The core idea of this is that there should be consistent interfaces to interact with different things built on top of Winter (i.e. modules, plugins & themes). Each of these elements will now have their class (i.e. Theme, PluginBase, ModuleServiceProvider) will now extend WinterExtension which will allow them to all interact with the update manager and other system functions directly.

In addition PluginManager, ThemeManager & ModuleManager (new) will all implement the ExtensionManager interface, which will allow for a consistant way to interact with all Winter extensions.

I.e. You will be able to call PluginManager::instance()->update($plugin) or ThemeManager::instance()->update($theme).

This also allows us to do some cool stuff like (only support for plugin installing is currently functional)

(new ExtensionSource(
    ExtensionSource::SOURCE_COMPOSER,
    ExtensionSource::TYPE_PLUGIN,
    composerPackage: 'jaxwilko/datamigrator'
))->install();

(new ExtensionSource(
    ExtensionSource::SOURCE_COMPOSER,
    ExtensionSource::TYPE_THEME,
    composerPackage: 'jaxwilko/custom-theme'
))->install();

(new ExtensionSource(
    ExtensionSource::SOURCE_LOCAL,
    ExtensionSource::TYPE_PLUGIN,
    code: 'winter.demo'
))->install();

(new ExtensionSource(
    ExtensionSource::SOURCE_LOCAL,
    ExtensionSource::TYPE_PLUGIN,
    path: 'plugins/winter/demo'
))->install();

By the end of the PR, the UpdateManager as is may no longer exist as is, instead each ExtensionManager will be responsible for it's existing functionality.

Requires storm branch: https://github.com/wintercms/storm/tree/wip/support-uploading-packages-update-manager-changes

@jaxwilko jaxwilko marked this pull request as draft November 29, 2024 18:41
@jaxwilko jaxwilko requested a review from LukeTowers November 29, 2024 18:41
Copy link
Member

@mjauvin mjauvin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

called method might return null, we need to use ?bool return type hint

modules/system/classes/extensions/PluginManager.php Outdated Show resolved Hide resolved
modules/system/classes/extensions/PluginManager.php Outdated Show resolved Hide resolved
@mjauvin
Copy link
Member

mjauvin commented Jan 5, 2025

@jaxwilko @LukeTowers should we add localization strings afterward or in this PR for info/warning/error messages ?

@mjauvin
Copy link
Member

mjauvin commented Jan 5, 2025

@jaxwilko requestUpdateList() does not exist anymore in UpdateManager class but is still being called in check() method.

@mjauvin
Copy link
Member

mjauvin commented Jan 5, 2025

Also getting this error when clicking "install themes" from the Updates & Plugins page:
image

@jaxwilko
Copy link
Member Author

jaxwilko commented Jan 5, 2025

@jaxwilko @LukeTowers should we add localization strings afterward or in this PR for info/warning/error messages ?

100%

All the strings in output atm are just placeholders

@jaxwilko
Copy link
Member Author

jaxwilko commented Jan 5, 2025

@jaxwilko requestUpdateList() does not exist anymore in UpdateManager class but is still being called in check() method.

Yeah, I've not finished fully migrating the backend yet, check for updates should work (but only supports composer packages) and interacting with plugins should work

@jaxwilko
Copy link
Member Author

jaxwilko commented Jan 5, 2025

Also getting this error when clicking "install themes" from the Updates & Plugins page:
image

Luke broke it, it's from his PR this one is based off... I'll look into it when i do more on the backend stuff :)

@jaxwilko
Copy link
Member Author

jaxwilko commented Jan 6, 2025

@bennothommo plz need sexy help!
image

<div title="Downloads" class="downloads">
<span class="product-badge"><i class="icon-download"></i></span>
{{product.downloads}}
</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably add the license as well

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, it'll have to come from the marketplace tho as the packagist api does not provide it

@LukeTowers
Copy link
Member

For future reference, @jaxwilko's JS for the event stream handling

const response = await fetch(window.location.href, {
                method: 'POST',
                headers: {
                    'Accept': 'text/event-stream',
                    'X-Requested-With': 'XMLHttpRequest',
                    'X-WINTER-REQUEST-HANDLER': 'onInstallPlugin',
                    'X-WINTER-REQUEST-PARTIALS': '',
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
                },
                body: data,
                redirect: 'follow',
                mode: 'same-origin',
            });

            if (response.status !== 200) {
                console.log('BAD');
                this.installing = false;
                return;
            }

            $.popup({
                size: 'installer-popup',
                content: `
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h4 class="modal-title">Installing ${this.product.name}</h4>
                    </div>
                    <div class="modal-body"></div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Blue Pill</button>
                        <button type="button" class="btn btn-primary" data-dismiss="modal">Red Pill</button>
                    </div>
                `
            });

            const popup = document.querySelector('.size-installer-popup');

            const chunk = async (response) => {
                const reader = response.body.getReader();
                const decoder = new TextDecoder();

                const parse = (str) => str.split("\n").filter((line) => !!line).map((line) => ({
                    type: line.substring(0, line.indexOf(': ')),
                    value: line.substring(line.indexOf(': ') + 2)
                }));

                const prepareMessage = (str) => {
                    ['INFO', 'ERROR'].forEach((status) => {
                        if (str.indexOf(status) === 0) {
                            str = `<span class="message-${status.toLowerCase()}">${status}</span> <pre>${str.substring(status.length + 1)}</pre>`;
                        }
                    });
                    return `<div class="install-message">${str}</div>`;
                };

                let done, value, message;
                while (!done) {
                    ({value, done} = await reader.read());
                    if (done) {
                        return;
                    }

                    message = parse(decoder.decode(value));

                    if (message[0].value === 'message') {
                        popup.querySelector('.modal-body').innerHTML += prepareMessage(message[1].value);
                    }

                    if (message[0].value === 'error') {
                        alert(message[1].value);
                    }
                }
            };

            await chunk(response);
            this.installing = false;

background-size: 50px 50px;
background-repeat: no-repeat;
background-position: 50% 50%;
background-image: url(/modules/system/assets/ui/images/loader-transparent.svg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way for this to be loaded via the ASSET_URL?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants