Simple Invoices logo
Simple Invoices - Home Simple Invoices - Download Simple Invoices - Demo Simple Invoices - Blog Simple Invoices - oOrum Simple Invoices - Wiki
Home Download Demo Blog Forum Wiki
    • CommentAuthorchuckt
    • CommentTimeFeb 26th 2007
     permalink
    Justin had mentioned a plugin system for SI in another post. I asked him about it in an off-line email and he suggested that I post here to get a discussion started on how that could work. So, I am posting my thoughts.

    Plugin systems all have certain things in common:
    * a way for the application to discover what plugins are available
    * a way to manage available plugins.
    * a well-defined interface that plugins use to communicate with the app.

    Many software implementations are little more than a plugin interface. It looks to me like this notion could apply to SI very effectively.

    This plugin interface will have to have a way to interface to the forms and menus, and the database. When you look at the source code, there are several source modules for an area of functionality.
    * add
    * manage
    * details
    * save
    I like this layout because I find it easy to understand. I am thinking that a plugin that wants to modify a form will have a function defined for each of these and the plugin manager will have the mechanics in place to call the function, if it is present, for each enabled plugin. So, for example, the add form will get new fields and the save functionality will know how to save the data in those fields. I am not very familiar with how to structure a database, but I think that the data for each plugin would need to be kept in separate tables and the plugin will need to know how to integrate the data into the reports and forms.

    One plugin that I am interested in is to have a wholesale discount capability, based on a flag in the customer data. Here is how that might work:

    There is an additional table that contains the discount tiers with a name of the tier and the % of the discount. There is a field in the customer data that has a reference to the tier. When the operator generates an invoice, the discount is applied to the total amount of the invoice before tax and shipping. There will have to be menu options associated with the plugin that provide an entry point for the discounts to be managed. It seems simple enough.

    I am very interested in getting this functionality working ASAP because I want to use it. If anyone is interested in helping, please post a reply. I think that this is a great thing that will allow SI to grow into whatever a person needs for their business.

    Cheers!
    -=ChuckT=-
    • CommentAuthorjustin
    • CommentTimeFeb 27th 2007
     permalink
    Hey Chuck,

    thanks for the great post!

    the information you detailed sounds very good, but would take me quite a while to do (anyone else interested in coding this)

    what i might do to get the ball rolling asap is make a kind of integrated plugins system

    basically an option screen listing all plugins and you select which ones you want and in the code it'll be something like

    if (discount_plugin_enabled)
    then ( add a discount drop down list in the item list ... )

    else
    do normal stuff

    this would have to be in all relevant screens

    pro:
    + quick to develop
    + the pluings would have to be included in the normal release - hopefully giving them more of a review by peers

    con:
    - not as extendable as a plugin system should be
    - the plugin would have to be included in the normal release and not downloaded and installed as required (a la Firefox addons)
    - any new sql stuff would have to be in the db by default but only used when plugin enabled
    - probably limit the amount of plugins that get developed
    - no well-defined interface for plugins

    im not sure how other php app do this kind of thing but will investigate

    what are your thoughts?

    notes for future:
    http://www.claroline.net/wiki/index.php/Plugin_system_modelisation
    http://codingforums.com/showthread.php?t=85051
    http://www.w3style.co.uk/?p=9
    http://www.sitepoint.com/forums/showthread.php?t=379440

    Cheers

    Justin
    • CommentAuthorchuckt
    • CommentTimeFeb 27th 2007
     permalink
    Thanks for the links. I lightly perused them. I will read them in more detail later tonight, but I believe that what I am thinking of is much more simple. Please bare with me. My knowledge of PHP is really limited at this point. (I am a C guy by trade).

    * there is no need to think about unloading a plugin.
    * we have a database to leverage

    The init part of the app would simply load a table with the names of the directories where the active plugins can be found.

    The plugin loader would then make an array of all of the interface functions for the plugin using a key.

    The plugin manager would then add that list to its array of plugins.

    After that, then anyplace that could use the plugin, then you would do all of the normal stuff, whatever that is, and then call the plugin manager something like this:

    // do all the normal stuff
    plugin_mgr("manage");

    -or perhaps-

    plugin_mgr("manage", func_that_does_the_normal_stuff); Where the plugin has complete control over the caller.

    All of the interface functions for different plugins in the array that are tagged as "manage" will then be called in the order in which they were loaded.

    Where I am really going with this is what C programmers call "programming by data structure". That means that the logic of the code is kept in a data structure rather than in the code. Here is a really contrived example of that:

    function one(void) {
    print("one");
    return 2;
    }

    function two(void) {
    print("two");
    return 0;
    }

    function three(void) {
    print("three");
    return 1;
    }

    $list = array($one, $two, $three);
    $val = 0
    while(1) {
    $val = $list[$val]();
    }

    The output would be
    one
    three
    two
    one
    three
    two
    ...

    So, the logic that implements the plugin is in the code that embodies the plugin, but the logic that calls it is located in the database as a data structure. The purpose of the plugin manager is to use that data to run the plugin correctly.

    I am not sure how to connect a string to a function name, but I think you can use them interchangably in PHP by just adding a () to the source line. like this:

    function buggers() {
    print("what?");
    }
    $func = "buggers";
    $func(); // print the word "what?"

    I am 100% sure that you can store a function in a variable and the interpreter will figure it out, though, I am not clear on the syntax of that. I.E. if $var = buggers; and $var = "buggers"; is the same thing in this case.
    (cant do that in C)

    I am going to look into the notion of how to map the functions in the plugin to the database and then get them back out into the array.

    Could someone please take a look at the existing code and try to come up with a list of functions that would be needed to make a complete and generic plugin. You would need a function name for every menu item, report, and form that any plugin might touch.

    For example, in my discount plugin, I would need a way to manage and save database entrys for the discount tiers. I would also need a way to generate the invoice so that it reflects the discount. And I would need a way to map a specific discount to a customer in the database. The database entry for the customer's discount tier would be kept in a separate table from the normal customer table.

    Sorry about the huge post. I know this is a lot of stuff, but I really think that adding plugins to SI should be fairly simple and straightforward. I need to find out what mechanisim to use to map the function names. I will post again when I do with some pseudo code. If I am out in the weeds, please let me know.... :mrgreen:

    Cheers!
    -=ChuckT=-
    • CommentAuthorjustin
    • CommentTimeFeb 27th 2007
     permalink
    Hey Chuck

    try whipping up some code, i'll give it a go to and we'll see what happens

    Cheers

    Justin
    • CommentAuthorchuckt
    • CommentTimeMar 1st 2007
     permalink
    I have been thinking about this is one detail that I did not mention before is that the plugins should be stored in a tree-like arrangement. That way, you could "connect" a plugin to a parent. It would streamline the plugin manager quite a bit and make it faster.

    I will be hacking some code this weekend for sure.

    -=ChuckT=-
    • CommentAuthorjustin
    • CommentTimeMar 2nd 2007
     permalink
    Hey Chuck,

    if i get some time this weekend i'll have a think about this

    cant wait to see your idea in code

    Cheers

    Justin
    • CommentAuthorchuckt
    • CommentTimeMar 3rd 2007
     permalink
    This snippet works just fine:

    <?php

    function test1() {
    print("here it is....<br/>");
    }

    $func = "test1";

    $func();

    function one() {
    print("one<br/>");
    return 2;
    }

    function two() {
    print("two<br/>");
    return 0;
    }

    function three() {
    print("three<br/>");
    return 1;
    }

    $list = array("one", "two", "three");
    $val = 0;
    $count = 0;

    for($count = 0; $count <= 5; $count = $count+1) {
    $val = $list[$val]();
    }


    ?>
    • CommentAuthorjustin
    • CommentTimeMar 4th 2007
     permalink
    looks very interesting chuck,

    having functions for each section - and for plugins just replacing certain funtions

    see if you can translate this into a real Simple Invoices example - i'd love to see how it works

    cheers

    justin
    • CommentAuthorchuckt
    • CommentTimeMar 4th 2007
     permalink
    It looks like what is really called for is a class for each section. That way the plugin can override that functionality and change its behavior. It looks as simple as calling the one from the plugin and then the one from the plugin will call the original. That way, the original code does not change. (I think)

    Does anyone know of a decent debugger for PHP?

    -=ChuckT=-
  1.  permalink
    the absolute best module aka plug-in system i have experienced is the system that is in the opensource VOIP project call [b]freepbx[/b], basically you have an admin section , and there is lots of plugins available , you have 3 options listed next to each plugin , i[b]nstall[/b] which installes plugin , [b]update[/b] which checks a server for the latest build of the plugin, and [b]uninstall[/b]. it works perfectly and the system is always easy to update to the latest code. i realise that this is not exactly what you are refering to here , and you are refering to the underlaying implemnetation of plugins <smile>
    • CommentAuthorchuckt
    • CommentTimeMar 4th 2007
     permalink


    Does anyone know of a decent debugger for PHP?



    I just got the trial version of Komodo. It simply rocks! I could not find a free one that seems useful.
    • CommentAuthorjustin
    • CommentTimeMar 4th 2007
     permalink
    thank darren,

    something like freepbx plugins or mambo/jooma would be a dream situation for Simple Invoices.

    cheers

    justin
    • CommentAuthorchuckt
    • CommentTimeMar 5th 2007 edited
     permalink
    -- post issue --
    • CommentAuthorjustin
    • CommentTimeMar 5th 2007 edited
     permalink
    -- post issue --
    • CommentAuthorchuckt
    • CommentTimeMar 5th 2007
     permalink
    I am going to start work on the plugin manager (PIM) for SI very soon. I will be looking into the freepbx stuff as a possible model. Here is a glossy design document for how I am thinking of doing that.

    I am planning to implement the PIM so that all of the other functionality of SI will be implemented as a PI (PlugIn).

    Basically, what I am thinking of right now is a strategy of using "callbacks". When a PI loads, it will register a callback for every function that it implements and they will actually be called by the PIM. When a request is submitted by the browser, which PI the request is for will be one of the items in the GET data, along with the arguments that the PI expects to receive. See the end for more about my version of how callbacks work.

    When the PIM registers a new block of code, it will expect to find certain functions (or classes) in the PI to use as the interface. I think it is reasonable (going the other way) to have a central location that emits the HTML for the page that is being requested and the PI will use services that are provided by the PIM to do that. That way, consistency in the HTML output is guarenteed. In other words, the PI will not emit any HTML; only the PIM will.

    PIs will be arranged and a tree structure so that you can manage situations where the behavior of selected sections changes, but leave others alone. All plugins will be "children" of a virtual user interface PI implemented by the manager to use as the root.

    PIs will have a notion of being dependent on another PI. This implys that any given PI is used to extend another PI. For example the "customer" PI will be dependent on the "root" PI. A PI that modifes the way that customers are handled will be dependent on the "customer" PI. For example, the PI that I want to have will allow one to specify a wholesale discount for a customer and that discount will be used when the invoice is generated. There also has to be a way to manage a list of discounts and assign one of them to a specific customer. Therefore, my PI would be dependent on "customers", "invoices", and "root". If you want more that one PI to modify the "customers", then there has to be a way to specify which one executes first. This can easily get complex.

    There are a few down sides to this dependency scheme:
    1) a PI will be able to execute code before or after its parent code executes, but if it wants to change the parent code, then it will have to replace the parent's function.
    2) a PI should be able to read the parent's data, but it should never change it. There is no "readonly" data attribute notion in PHP AFAIK. This is bound to get abused.
    3) a PI can load at the same level in the tree as another PI. If it does, then it cannot have access to the other's data except through the database. It will, however, be physically possible and that is a bad thing for reliability.
    4) when a page is processed, all of the PI dependencies will have to be processed first, requiring significant database access. This could be a speed problem with a complex tree.
    5) root-level PIs will have to "own" a menu. That will require changes to the UI as it is. Children of a root PI will be able to modify the parent's menu.
    6) some sort of install program or option will be required in order to manage the dependencies of PIs. It will have to be persistent in the life of the program and that is a security risk.
    7) if a PI has another that is dependent on it, and you try to disable it, an error should be generated giving specific information about the dependencies. That could get complex (and time consuming) if a bunch of interdependent PIs are loaded.
    n) there are more that I have not though of yet.

    Here are some of the up sides:
    1) you will be able to have a SimpleInvoices that is as simple or complex as you want it to be.
    2) virtually any PI that is compatible with the interface will be usable. That means that SimpleInvoices could be adapted to keep track of your DVDs if you write a PI for that. (just an example that is very different from SI's mission)
    3) it will be possible to have a robust admin functionality that can really do stuff like themes from a menu.
    4) you can have a reports PI and then add different reports as additional PIs. (Implications for the template system)

    I could go on and on about the benefits and problems with this scenario. I am really very interested in hearing what other have to say on it.

    That is about all I can think of now. Better flame me soon or forever STFU.

    Cheers!
    -=ChuckT=-

    PS: callbacks:
    If a manager wants to call a piece of code, but it has no idea what that code is or what it is called, then it can make a blank pointer to reserve a spot for that entry point. When the thing that wants the manager to call it gets loaded, it can register a pointer to the thing that it wants done with the manager. The manager stores the pointer in the place where it has reserved. When the manager wants to run that code then is "calls back" the thing that loaded via that pointer.
    • CommentAuthorchuckt
    • CommentTimeMar 5th 2007 edited
     permalink
    -- post issue --
    • CommentAuthorjustin
    • CommentTimeMar 5th 2007
     permalink
    • CommentAuthorstuey
    • CommentTimeMar 8th 2007
     permalink
    Hold fire a little, lets take a stock

    WHats the point of a plug-in?

    In my books it would offer some functionality which is not the system used by a smaller user base.

    Some of the items in the plugin section I would see as

    *Create monthly recurring invoices
    *Stock Manager
    *Cash flow/Money Managers
    *Skins

    These all work and add functionality to the system which was never envisaged by the core number of users.

    Changes such as price discounts are a little more complex, the way I see it, that should be part of the invoice/customer base?

    The same with some of the tax information etc.
    I think you/we need to set some ground rules on how the plugins should operate?

    Heres two.

    Plugins should be configured from a single Menu Drop down on the main nav
    Plugins should not require changes to the main tables except for the use of Custom fields to link data.

    This way, If you/like certain functions or its a plugin which is installed by every one you should be able to port it striaght in. without having to re-write all your TSQL scripts etc
    • CommentAuthorjustin
    • CommentTimeMar 9th 2007
     permalink
    Hey Stu,

    thanks for the post,

    the main point of plugins is so that people can extend Simple Invoices to suit there own requirements without having to make there own version of Simple Invoices (and thus there changes not making it back to us so others can use it,etc..) - basically to allow people to extend simple invoices without having to 'fork' it, and to encourage sharing of modifications

    re ground rules
    - yep, once this thing gets going we'll definitely have to have some rigid rule
    - and i think the main one will be as you said 'not altering the main tables' - maybe each plugin has its own table(if it requires one) to do what it wants to
    - and the plugins will be managed by chucks PIM - which hopefully will be a simple screen listing all plugins with an enable/disable option

    Cheers

    Justin
    • CommentAuthorjohanb
    • CommentTimeJul 23rd 2008
     permalink
    Hi,

    Is there any news on how far the plugin system is and when the recurring invoices will be in place as I really like simple invoices, but without the recurring invoices system, my hosting company cannot really work with it and I want a system where I don't need much admin work to be done and that the system is doing it automatically.

    Please let me know

    Regards,
    Johan
    • CommentAuthorjustin
    • CommentTimeJul 23rd 2008
     permalink
    Hey Johan,

    the plugin system has morphed into the Simple Invoices extension system
    - refer:
    -- http://www.simpleinvoices.org/wiki/extensions
    -- http://www.simpleinvoices.org/wiki/extensions_faq

    extension system is still very much a work in progress - but its working

    re recurring invoices
    - no progress has been made unfortunately
    - i understand your need for this feature
    - hopefully sometime in the not to distant future i can have a look at implementing this

    Cheers

    Justin