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.

Override functions without bootstrap

I want to override functions from /library/core/class.format.php and /library/core/functions.general.php and found several discussions concerning that. It works with /conf/bootstrap.after.php but I do not like that.

First one tells me not use bootstrap, but to do that with a plugin. I'd prefer that because I want to offer a plugin solution not a copy and paste instruction: http://vanillaforums.org/discussion/22107/can-any-function-be-overridden-via-bootstrap

What I have right now uses a "Gdn::FactoryInstall". How would I use that in my plugin? I've tested it in my Setup function but that didn't work...

Best Answers

Answers

  • One of my plugins overrides classes such as the ActivityModel by simply declaring them, with the same file and class name. I'm not 100% sure it will work with every class, but you can try:

    • Name your custom file class.format.php and put it into the plugin's folder.
    • Clear the cache.
    • Reload the site and see if your class is picked up instead of the core one.
  • Nope, it does not :( I just tested it on class.format.php, don't know if it might work on functions.general.php.

    Here's what I've got working in bootstrap.after.php:

    <?php
    class UmlautMentionsFormatter {
       public function GetMentions($String) {
    decho('MyGetMentions');   
          $Mentions = array();
    
          // This one grabs mentions that start at the beginning of $String
          preg_match_all(
             '/(?:^|[\s,\.>])@('.ValidateUsernameRegex().')\b/i',
             $String,
             $Matches
          );
          if (count($Matches) > 1) {
             $Result = array_unique($Matches[1]);
             return $Result;
          }
          return array();
       }
    
       public function FormatMentions($Mixed) {
    decho('MyFormatMentions');
          if (!is_string($Mixed)) {
             return To($Mixed, 'Mentions');
          }
    
          // Handle @mentions.
          if(C('Garden.Format.Mentions')) {
             $Mixed = preg_replace(
                '/(^|[\s,\.>])@('.ValidateUsernameRegex().')\b/i',
                '\1'.Anchor('@\2', '/profile/\\2'),
                $Mixed
             );
          }
    
          // Handle #hashtag searches
          if(C('Garden.Format.Hashtags')) {
             $Mixed = preg_replace(
                '/(^|[\s,\.>])\#([\w\-]+)(?=[\s,\.!?]|$)/i',
                '\1'.Anchor('#\2', '/search?Search=%23\2&Mode=like').'\3',
                $Mixed
             );
          }
    
          // Handle "/me does x" action statements
          if(C('Garden.Format.MeActions')) {
             $Mixed = preg_replace(
                '/(^|[\n])(\/me)(\s[^(\n)]+)/i',
                '\1'.Wrap(Wrap('\2', 'span', array('class' => 'MeActionName')).'\3', 'span', array('class' => 'AuthorAction')),
                $Mixed
             );
          }
    
          return $Mixed;
       }
    
    }
    Gdn::FactoryInstall('MentionsFormatter', 'UmlautMentionsFormatter', NULL, Gdn::FactoryInstance);
    

    I have now hooked Base_Render_Before to execute "Gdn::FactoryInstall('MentionsFormatter', 'UmlautMentionsFormatter', NULL, Gdn::FactoryInstance);" each time and included the above code (without the last line) in setup.
    That seems to work, but I'm unsure if it is "right" from a developers point of view.

  • edited December 2013

    Based on the example, I fail to see the purpose of using the factory installer. The UmlautMentionsFormatter is just a simple class and it doesn't have anything to do with the core formatter. You can simply load the class in the code, as needed:

    $Formatter = new UmlautMentionsFormatter();
    $Formatter->FormatMentions();
    
  • Sorry, I should have explained what I try to achieve before asking how to...

    I want to replace the GetMentions from functions.general.php and Mentions from class.format.php
    If doing it the way above (which btw I have stolen from that discussion that I do not really understand http://vanillaforums.org/discussion/18144/最新版本中文用户名解决方案) it works.

    I want it to replace the core formatter!

  • R_JR_J Admin
    edited December 2013

    @Lincoln: I'm toying around with something like @"test user" or @<test user> and hope to find an answer to the ever ongoing discussion concerning Vanillas mention feature ;)

    Right now I'm here but it is not working

          // Handle @mentions.
          if(C('Garden.Format.Mentions')) {
             $Mixed = preg_replace(
    //            '/(^|[\s,\.>])@('.ValidateUsernameRegex().')\b/i',
                '/(^|[\s,\.>])@'.C('Plugins.MentionsPlus.MentionStart').'('.ValidateUsernameRegex().')'.C('Plugins.MentionsPlus.MentionStop').'\b/i',
                '\1'.Anchor('@\2', '/profile/\\2'),
                $Mixed
             );
          }
    

    C('Plugins.MentionsPlus.MentionStart') is ß? right now. I just see where it get's me...

    By the way: I'm using this nice site to test my regexes https://www.debuggex.com

  • HA! Well, I haven't tried GetMentions yet, but the mentionsformatter works like a charme :)
    Does anybody mind trying mentions+?
    https://github.com/R-J/MentionsPlus

    I think I'll make it an official addon when I've proven the mentioning function itself is working...

  • LincLinc Admin
    edited December 2013

    We've done a plugin that modifies mentions to allow spaces by using double quotes, much like your example above. If you get stuck on how to do it without hacking core, let me know and I'll look up how it was done. I can't open source it because it was client-specific (and not how we want to implement it in core or support at all) but I don't mind pointing the way. ;)

  • That's truely great to hear! In fact I have a problem that I can not solve. When I look at Gardens function Mentions

       public static function Mentions($Mixed) {
          if (!is_string($Mixed)) {
             return self::To($Mixed, 'Mentions');
          } else {
             // Check for a custom formatter.
             $Formatter = Gdn::Factory('MentionsFormatter');
             if (is_object($Formatter)) {
                return $Formatter->FormatMentions($Mixed);
             }
    

    and GetMentions

    if (!function_exists('GetMentions')) {
       function GetMentions($String) {
          // Check for a custom mentions formatter and use it.
          $Formatter = Gdn::Factory('MentionsFormatter');
          if (is_object($Formatter)) {
             return $Formatter->GetMentions($String);
          }
    

    it looks to me as if I would only need a class of my own with functions FormatMentions and GetMentions.
    The way I've done it (calling Gdn::FactoryInstall('MentionsFormatter', 'UmlautMentionsFormatter', NULL, Gdn::FactoryInstance); in Base_Render_Before`works perfectly for FormatMentions, but it does not work for GetMentions...
    Obviously Base_Render_Before isn't the right place to hook but where would my FactoryInstall be placed best?

    I'm quite happy with the FormatMentions part right now. I'll try to do the work with just one preg call but that's just cosmetic.
    I've even seen in a Zombie thread that someone asked for a translation for /me. If I get it right, it will be a snap with this plugin, too.

    So all I need now is that my own GetMentions function is called...

  • I believe you want to put the factory install line in your plugin's __construct() method.

    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.

  • Factory, construct... I'm beginning to feel like the passenger that has to land the Boing after the pilot had a heartattack :D

    It might be about time to learn the basics...

    All right, will try it out and give feedback!

  • Boing crashed ;)

    @hgtonight: Nope, putting the factory call magic into __construct doesn't work either.

    By now the only thing working was putting it into /conf/bootstrap.after.php which I wanted to avoid in order to make it possible to put everything into one plugin.
    I think I'll make a pre-release with the drawback that user have to alter/create bootstrap file manually so that I've got a chance for peregrines vote for plugin of the year :)

    I've tried Base_Render_Before and and __construct until now, using Vanilla 2.0.18.8. I will try the __construct way with 2.1b2 later...

  • Also not working with 2.1b2, but plugin is released nevertheless! http://vanillaforums.org/addon/mentionsplus-plugin

  • You just need to define GetMentions in your plugin file, outside the class so it is a function, not a method.

  • My plugin now looks like this:

    $PluginInfo['MentionsPlus'] = array( ... );
    class MentionsPlusPlugin extends Gdn_Plugin { ... }
    function GetMentions($String) { ... }
    

    and I get following error message: Fatal error: Cannot redeclare GetMentions() (previously declared in /vanilla_2_0_18_8/library/core/functions.general.php:1105)
    ~~~

  • Balls. I guess functions.general is getting called too soon. What a pain.

  • R_JR_J Admin
    edited December 2013

    Don't mind. It works with bootstrap.after.php and I made an epic README on how to use it that way. But thanks for your efforts!

    I simply don't like the idea that it is not possible to use the comfort of the standard Vanilla plugins where enabling and disabling does all the work. In order to make my plugin fool proof, I would now also have to make a popup note on plugin disable to remember the user to manually clean up bootstrap, I guess and that is the dirty work I don't like to do... :\
    Perhaps I just write that step in the README, too and don't code anything at all concerning OnDisable

  • @R_J said:

    I think I'll make a pre-release with the drawback that user have to alter/create bootstrap file manually so that I've got a chance for peregrines vote for plugin of the year :)

    now, we all we need is the casual users who haven't voted to vote :smile:

    http://vanillaforums.org/discussion/25578/nominations-for-best-plugin-created-this-year

    I may not provide the completed solution you might desire, but I do try to provide honest suggestions to help you solve your issue.

Sign In or Register to comment.