Please upgrade here. These earlier versions are no longer being updated and have security issues.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

Doesn't work for NewDiscussion Module

This plugin doesn't remove NewDiscussionModule when I when I remove the module via the delete button.

Evidence :

«1

Comments

  • This doesn't let you add or remove modules. It can only change the sort order.

    You will have to remove it with a CSS rule.

    .BoxNewDiscussion { display:none; }
    
  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    While it doesn't add modules (I understand why) I was expecting to see the defined modules in the list. I noticed that the "Tagmodule" is not on the list so I looked at the code and realized it depends on the definitions in config.php and so I took a peek at my definitions and indeed the tagmodule wasn't there even though it was on the side panel. I added it in the Module Sort configuration screen and I was able to sort the module list as expected.

    So the question is whether there is a better more reliable way than the config.php to generate the module list?

  • BleistivtBleistivt Moderator

    @rbrahmson plugins can add modules dynamically. There is no way to generate a list of "active" modules.
    Consider this:

    if ($weirdCondition) {
        $sender->addModule('MoonShineModule');
    }
    
  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    Yes, I understand that and I've done that myself (inbox panel plugin to name one), my question is whether there is a way around the problem (e.g. find when plugins add panels and save in config.php so that later module sort can list them. Yea, the admin has to first cause the action that trigger the plugin adding the panel but it solves the problem of finding the name of the added module [I assume not all admins are developers capable of finding the information in the source]).

    Even better is to let plugins declare in Plugininfo the name of possible modules they may be adding to the side panel (voluntary, I know...)

    Sometimes there's no perfect solution (but perhaps I'm not sufficiently knowledgeable or creative... )

  • vrijvlindervrijvlinder Papillon-Sauvage MVP

    Since plugins may contain modules, you would need to know the name of the module to add to the sort list.

    A plugin could check what modules there are and provide a list so you can add it to the config.php

    I personally just add the modules to the config.php list of modules in the order I want.

  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    @vrijvlinder wrote:

    you would need to know the name of the module to add to the sort list.

    Obviously, and I mused above about two possible approaches that somewhat alleviate that need
    @vrijvlinder wrote:

    I personally just add the modules to the config.php list

    Well, but that requires the admins to know the name of the modules which in turn requires them to look at the code - not all are developers and know how to do that.

  • R_JR_J Ex-Fanboy Munich Admin

    @Bleistivt said:
    @rbrahmson plugins can add modules dynamically. There is no way to generate a list of "active" modules.
    Consider this:

    if ($weirdCondition) {
        $sender->addModule('MoonShineModule');
    }
    

    @rbrahmson said:
    Yes, I understand that and I've done that myself (inbox panel plugin to name one), my question is whether there is a way around the problem (e.g. find when plugins add panels and save in config.php so that later module sort can list them.

    There is an easy and an at least for a plugin developer obvious way to do so. Do you want me to provide you with code or is my hint (citing the code example of bleistivt) enough to get you started? ;)

  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    Hi @R_J,
    Even though I was merely suggesting that @bleivist would consider a solution, your post prompted me to look at the source and I saw that there are two hooks (BeforeAddModule and AfterAddModule) in the addModule function, so in theory the dynamic addition of modules could be intercepted and the relevant info could be saved in config.php. However I did not see that the function parameters were set in EventArguments for the hook...

    Out of curiosity I also looked at the different variables pointed by $Sender in DiscussionsController_Render_Before and did not see anything pointing to the module list (at least nothing unprotected).

  • R_JR_J Ex-Fanboy Munich Admin

    Okay, it is not that obvious ;-)

    You've started with looking at controller->addModule() which is the right start. The AfterAddModule would be a good thing to look at, but it doesn't have the Module name as an argument. So the most easiest way wouldn't work. Maybe there is another way to get the name of the module, I don't know and I don't care since there is a more resource friendly way to get what you need.

    What does addModule do? Does it add a module? Yes and no: if you look at the source, you see that it adds an asset by calling controller->addAsset(). When you look at that function, you do not find usable EventArguments either. So see what it does: it fills an array $this->Assets. And look at the comments of this method!

         * @param string $AssetName The name of the asset being added. This can be
         * used later to sort assets before rendering.
    

    We must be close!

    And indeed, we only have to find out how to get that array. Which function is always called when everythingis set up and only has to be rendered? You know this already: base_render_before. It would only be called once instead of multiple times like the events in addModule

    So try this one out:

    public function base_render_before($sender) {
        decho(array_keys((array)$sender->Assets['Panel']));
    }
    

    It will give you the names of each Asset in the Panel, which basically should be our modules. Naming conventions say that a module should end with the string "Module" so just to be 100% sure, you can only add those array elements, that end with "Module".

  • R_JR_J Ex-Fanboy Munich Admin

    I don't know the module sort plugin, but I would assume it only changes the config setting. If you add such a functionality to it, it would use resources over and over and over. That's why I think it is wise not to bundle it with the plugin. It is not worth the overhead.

    Maybe there is a way to get all available class names, but that is advanced php stuff and that's nothing I know of :(
    This would be a much better approach since such a scan could be started from out of the config screen and as such wouldn't bin any resources when simply browsing the forum.

  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    Few observations:

    • when I dumped $Sender I didn't see the modules, but I didn't use base render, so my fault...
    • -yea, I do realize that doing things in the config screen costs less. That's why I "proposed" and additional field in Plugininfo
    • getting defined class names : get_declared_classes().
    • all of this is a neat exercise (at least for me)
  • R_JR_J Ex-Fanboy Munich Admin

    get_declared_classes will not work: modules are loaded dynamically. Try it yourself:

        public function settingsController_nope_create($sender, $args) {
            decho(get_declared_classes());
        }
    

    And the look at /settings/nope
    There are a lot of classes there but not e.g. the NewDiscussionModule

  • hgtonighthgtonight ∞ · New Moderator
    edited May 2016

    Iterate over get_declared_classes() and see if it implements Gdn_IModule.

    Do this once on setup and have a button to refresh the list. This should get you every possible module that is in the Autoloader's path that is enabled.

    Sample code from Yaga:

    foreach(get_declared_classes() as $className) {
      if(in_array('YagaRule', class_implements($className))) {
        $Rule = new $className();
        $TempRules[$className] = $Rule->Name();
      }
    }
    

    Replace 'YagaRule' with 'Gdn_IModule' and you can generate the list of modules.

    EDIT - I just remembered the autoloader is called only when a class that isn't declared is requested. I will have to think on this.

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    So the only reasonable approach I can see is the Plugininfo...
    But then I would admit that there may be more fields to consider to add there and also to support some of them in the plugin directory require Vanilla change (different discussion I guess)

  • hgtonighthgtonight ∞ · New Moderator
    edited May 2016

    This is the best idea I could come up with. Get a list of php files with module in the name, include them, then iterate over the declared classes.

    $dir = new RecursiveDirectoryIterator(PATH_ROOT);
    $i = new RecursiveIteratorIterator($dir);
    $files = new RegexIterator($i, '/^.+module.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
    
    foreach($files as $filename) {
      include_once $filename;
    }
    
    $moduleNames = [];
    foreach(get_declared_classes() as $className) {
      if(in_array('Gdn_IModule', class_implements($className))) {
        $moduleNames[] = $className;
      }
    }
    
    var_dump($moduleNames);
    

    I would do this only upon user initiation in the dashboard.

    EDIT: Below is working code

    $dir = new RecursiveDirectoryIterator(PATH_ROOT);
    $i = new RecursiveIteratorIterator($dir);
    $files = new RegexIterator($i, '/^.*module.*\.php$/i', RecursiveRegexIterator::GET_MATCH);
    
    foreach($files as $filename) {
        if(strpos($filename[0], 'views') === false) {
            include_once $filename[0];
        }
    }
    $moduleNames = [];
    $classes = get_declared_classes();
    foreach($classes as $className) {
      if(in_array('Gdn_IModule', class_implements($className))) {
        $moduleNames[] = $className;
      }
    }
    var_dump($moduleNames);
    

    Output of a fresh install with no plugins enabled except Yaga:

    array (size=48)
      0 => string 'Gdn_Module' (length=10)
      1 => string 'MenuModule' (length=10)
      2 => string 'HeadModule' (length=10)
      3 => string 'SideMenuModule' (length=14)
      4 => string 'AddPeopleModule' (length=15)
      5 => string 'ClearHistoryModule' (length=18)
      6 => string 'InboxModule' (length=11)
      7 => string 'InThisConversationModule' (length=24)
      8 => string 'NewConversationModule' (length=21)
      9 => string 'ActivityFilterModule' (length=20)
      10 => string 'ConditionModule' (length=15)
      11 => string 'ConfigurationModule' (length=19)
      12 => string 'GuestModule' (length=11)
      13 => string 'MeModule' (length=8)
      14 => string 'MessageModule' (length=13)
      15 => string 'PagerModule' (length=11)
      16 => string 'MorePagerModule' (length=15)
      17 => string 'NavModule' (length=9)
      18 => string 'ProfileFilterModule' (length=19)
      19 => string 'ProfileOptionsModule' (length=20)
      20 => string 'RecentActivityModule' (length=20)
      21 => string 'RecentUserModule' (length=16)
      22 => string 'SettingsModule' (length=14)
      23 => string 'SignedInModule' (length=14)
      24 => string 'SiteNavModule' (length=13)
      25 => string 'SiteTotalsModule' (length=16)
      26 => string 'ToggleMenuModule' (length=16)
      27 => string 'TraceModule' (length=11)
      28 => string 'UserBanModule' (length=13)
      29 => string 'UserBoxModule' (length=13)
      30 => string 'UserInfoModule' (length=14)
      31 => string 'UserPhotoModule' (length=15)
      32 => string 'BookmarkedModule' (length=16)
      33 => string 'CategoriesModule' (length=16)
      34 => string 'CategoryFollowToggleModule' (length=26)
      35 => string 'CategoryModeratorsModule' (length=24)
      36 => string 'DiscussionFilterModule' (length=22)
      37 => string 'DiscussionsModule' (length=17)
      38 => string 'DiscussionSorterModule' (length=22)
      39 => string 'DraftsModule' (length=12)
      40 => string 'NewDiscussionModule' (length=19)
      41 => string 'PromotedContentModule' (length=21)
      42 => string 'BadgesModule' (length=12)
      43 => string 'BestFilterModule' (length=16)
      44 => string 'LeaderBoardModule' (length=17)
      45 => string 'Gdn_ModuleCollection' (length=20)
      46 => string 'TagModule' (length=9)
      47 => string 'InThisDiscussionModule' (length=22)
    

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    Wow,definitely more than is ever likely to be usable for this plugin. Oh, well, was an interesting concept...

    And not to forget, thanks to all the good feedback and ideas.

  • vrijvlindervrijvlinder Papillon-Sauvage MVP

    Yes, that is why I prefer to put it in manually because there is an infinite number of possible modules besides the core ones.

  • R_JR_J Ex-Fanboy Munich Admin

    @hgtonight said:
    This is the best idea I could come up with. Get a list of php files with module in the name, include them, then iterate over the declared classes.

    I think that this is more a proof of concept, but no good idea.

    There are numerous modules that might never be shown because they belong to not activated plugins/addons or will not be rendered in the panel. Also including something like InboxModule doesn't make sense.
    A normal admin will not know even what the half of those modules is good for.

    And from the UX aspect, I think it would be horrible. Presenting a long list of text to sort the modules is a burden, not a help. It would be easier to pick them separately if needed.
    The only nice interface for sorting modules I could think of, would be to load a javascript with each page that attaches a handler to each module so that the order in the DOM is changed (this way giving a direct visually feedback) and which writes the current order in the config.

    It could be realized as two plugins: one that shows a config setting "[ ] show inline module sort option" and a second one with the plugin configuration 'Hidden' => true (as it is used in the Facebook plugin) which adds the js to each page. Ticking the checkbox in plugin A enables plugin B and unticking it disables plugin B. That way no resources are wasted! Whoa, I'm feeling like a genius right now!

    Another nice (purely theoretical!) approach would be to render each module and show them in the dashboard so that sorting can be done made by the real module and not the name of the module. But sadly modules need a context and it is impossible to see their output when you don't know which input they expect (and that is not standardized)

  • rbrahmsonrbrahmson "You may say I'm a dreamer / But I'm not the only one" NY ✭✭✭

    I was toying with the idea somewhat similar to your two plugins but a bit different. The second, called "collector" would be active temporarily and collect the module names and save in config.php and self disable to reduce overhead. That reduces the problem to the question of how to decide when to stop collection, an easier task I think. It could be deactivated by date expiration, count, etc, and reactivated temporarily by the main plugin.

    All is theoretical, I assume. But at least for me a learning experience, so thanks.

  • R_JR_J Ex-Fanboy Munich Admin

    I also enjoy thinking about what is possible. No matter if realizing even a single line of code would make sense or not :mrgreen:

Sign In or Register to comment.