{"id":461,"date":"2021-01-13T12:43:45","date_gmt":"2021-01-13T11:43:45","guid":{"rendered":"https:\/\/daniel.liljeberg.io\/?page_id=461"},"modified":"2021-01-15T14:27:26","modified_gmt":"2021-01-15T13:27:26","slug":"dlicore-2","status":"publish","type":"page","link":"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/","title":{"rendered":"dliCore"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This is a copy of the README that comes with dliHelper and dliCore<\/p>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_84 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-6a20d58a967dd\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-6a20d58a967dd\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#cli_tool\" >CLI tool<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#create_new_plugin\" >Create new plugin<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#packaging_a_plugin\" >Packaging a plugin<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#dlicore_quick_reference\" >dliCore Quick Reference<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#hooks\" >Hooks<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#docblock_hook_definitions\" >DocBlock Hook definitions<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#registering_plugins_own_hooks\" >Registering Plugins own hooks<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#controllers\" >Controllers<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#views\" >Views<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#fragments\" >Fragments<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#tables\" >Tables<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#models\" >Models<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#automatic_caching_of_model_objects\" >Automatic caching of model objects<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#avoiding_caching_of_certain_properties_of_an_object\" >Avoiding caching of certain properties of an object<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#emails\" >Emails<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#dependencies\" >Dependencies<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#optional_dependencies\" >Optional Dependencies<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#processes\" >Processes<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#jobs\" >Jobs<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#plugin_storage\" >Plugin storage<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#plugin_storage_functions\" >Plugin storage functions<\/a><ul class='ez-toc-list-level-4' ><li class='ez-toc-heading-level-4'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#plugin_storage_related_hooks\" >Plugin storage related Hooks<\/a><\/li><\/ul><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#additional_components\" >Additional Components<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/#docker_development_image\" >Docker Development Image<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\" id=\"clitool\"><span class=\"ez-toc-section\" id=\"cli_tool\"><\/span>CLI tool<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In \/bin you will find a script dliHelper.php that can help you to get started even faster. Note that this tool is just started and is subject to change \ud83d\ude42<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Usage:<\/strong> dliHelper.php &#8211;method=&#8221;methodName&#8221; [args]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>&#8211;method<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>help<\/li><li>createPlugin<br>&#8211;internalName: Used for namespace, class name etc, i.e. myThing<br>&#8211;pluginName: Public name of the plugin<br>&#8211;description: Description of plugin<br>&#8211;author: Name of author<br>&#8211;supportEmailAddress: Mail address to send support inquires to<\/li><li>packagePlugin<br>&#8211;pluginName: Plugin to package<br>&#8211;location: Folder to put package in<\/li><li>generateClassMap<br>&#8211;pluginName: Plugin to generate class map for<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"create_new_plugin\"><\/span>Create new plugin<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To create a new plugin named Foo, just type<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">.\/dliHelper.php --method=\"createPlugin\" --pluginName=\"myFoo\" --description=\"Foo is a plugin built using dliCore\" --author=\"Daniel Liljeberg\" --supportEmailAddress=\"support@foo.com\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You will now have a new plugin in you <strong>oc-contents\/plugins<\/strong> folder named <strong>myFoo<\/strong>. It will contain a few folders and files. Your plugin will live under the <strong>namespace myFoo<\/strong>, or whatever name you gave your plugin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>index.php<\/strong> is created for you and handles registering your plugin with the dliCore PluginManager. This does not have to be touched except for editing Osclass specific plugin information found in the information block at the top.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Your actual plugin is found in <strong>myFooPlugin.php<\/strong> and is an instance of a Plugin class called <strong>myFooPlugin<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The plugin will also be configured to allow users to click on the question mark saying &#8220;Problems with this plugin? Ask for support.&#8221; in the plugins list in the admin area. That will take them to a form where they can request support which will be sent to the mail you provided as the <strong>&#8211;supportEmailAddress<\/strong> argument.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can choose to remove this by altering the &#8220;Support URI&#8221; string in the <strong>index.php<\/strong> file information block.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Also, the plugin&#8217;s &#8220;configure&#8221; link will point to the indexAction of the AdminController. The view associated with the controller can be found under views\/Admin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To change which controller the configure link points to you alter the <strong>$_configAction<\/strong> variable of your plugin. If no configuration should be visible you can delete this variable and you are then also free to delete the<br>views\/Admin folder and the Controllers\/AdminController.php file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"packaging_a_plugin\"><\/span>Packaging a plugin<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Packaging a plugin helps you get it ready for distribution. It&#8217;s completely optional to use but it aids you with several things.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">.\/dliHelper.php --method=\"packagePlugin\" --pluginName=\"fooBar\" --location=\"\/opt\/osclass-production\/oc-content\/plugins\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The above command is used to package the plugin <strong>fooBar <\/strong>located in <strong>osclassroot\/oc-contens\/plugins\/fooBar<\/strong> and deploy it to<br><strong>&#8220;\/opt\/osclass-production\/oc-content\/plugins&#8221;<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">During packaging several things will be checked for you.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>If your plugin has dependencies to other dliCore based plugins you will be given the option to package these to.<br>This helps make sure you don&#8217;t rely on functionality in a dependency that has not been released.<\/li><li>If your plugin has dependencies to other dliCore based plugins their versions will be checked and if they differ<br>from your dependency you will be notified and given the option to update your dependency.<\/li><li>If your plugin is under SVN control and local modifications exist you will be notified and given the option to<br><strong>&#8220;diff&#8221;<\/strong> the modifications<br><strong>&#8220;commit&#8221;<\/strong> the modifications<br><strong>&#8220;ignore&#8221;<\/strong> the modifications<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Once everything is in a clean state and all checks out your plugin is deployed to your given folder. This can be a folder where you just collect the plugins to zip and upload or another clean OsClass installation used to<br>test your plugin from a clean state. If your plugin was under SVN control a &#8220;checkout&#8221; will be made to the destination and the .svn folder removed. If your plugin is not under SVN control a normal copy will occur.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After this is done you will be given the option to generate a class map file for you plugin. This speeds up auto loading and is recommended for production environments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lastly, a file containing information regarding the plugin such as dependencies on other plugins etc is created to assist you when writing information about your plugin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note that you should have the following php extensions enabled in order to use this tool<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>php-mysqli<\/li><li>php-simplexml<\/li><li>php-tokenizer<\/li><li>php-pdo_sqlite<\/li><li>php-zlib<\/li><li>php-json<\/li><li>php-phar<\/li><li>php-iconv<\/li><li>php-mbstring<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The packager presents the option to minify js and css files so you can work with unminified version during development and have the the packager minify them for your release. Optionally you can also have the packager go through a lot of different file types using Leanify and try to compress them. These include documents, base64 encoded image data in html files, images etc.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">APK file (.apk)\n\n    It is based on ZIP.\n    Note that modifying files inside APK will break digital signature.\n    To install it, you'll have to sign it again.\n    If you don't want to modify any files inside APK, use -d 1 option.\n\n\nComic book archive (.cbt, .cbz)\n\n    cbt is based on tar. cbz is based on ZIP.\n\n\nMicrosoft Office document 2007-2013 (.docx, .xlsx, .pptx)\n\n    It is based on XML and ZIP.\n    Office document 1997-2003 (.doc, .xls, .ppt) is not supported.\n\n\nData URI (.html .htm .js .css)\n\n    Looks for data:image\/*;base64 and leanify base64 encoded embedded image.\n\n\nDesign Web Format (.dwf, dwfx)\n\n    It is based on ZIP.\n\n\nEPUB file (.epub)\n\n    It is based on ZIP.\n\n\nFictionBook (.fb2, .fb2.zip)\n\n    It is based on XML.\n    Leanify embedded images.\n\n\nGFT file (.gft)\n\n    It's an image container format found in Tencent QQ.\n    Leanify the image inside.\n\n\ngzip file (.gz, .tgz)\n\n    Leanify file inside and recompress deflate stream.\n    Remove all optional section: FEXTRA, FNAME, FCOMMENT, FHCRC.\n\n\nIcon file (.ico)\n\n    Convert 256x256 BMP to PNG.\n    Leanify PNG inside, if any.\n\n\nJava archive (.jar)\n\n    It is based on ZIP.\n\n\nJPEG image (.jpeg, .jpg, .jpe, .jif, .jfif, .jfi, .thm)\n\n    Remove all application markers (e.g. Exif (use --keep-exif to keep it), \n    ICC profile, XMP) and comments.\n    Optimize with mozjpeg.\n\n\nLua object file (.lua, .luac)\n\n    Remove all debugging information:\n\n    Source name\n    Line defined and last line defined\n    Source line position list\n    Local list\n    Upvalue list\n\n\nOpenDocument (.odt, .ods, .odp, .odb, .odg, .odf)\n\n    It is based on XML and ZIP.\n\n\nPE file (.exe, .dll, .ocx, .scr, .cpl)\n\n    Leanify embedded resource.\n    Remove Relocation Table in executable file.\n    Remove undocumented Rich Header.\n    Overlap PE Header and DOS Header.\n\n\nPNG image (.png, .apng)\n\n    Remove all ancillary chunks except for:\n\n    tRNS: transparent information\n    fdAT, fcTL, acTL: These chunks are used by APNG\n    npTc: Android 9Patch images (*.9.png)\n    Optimize with ZopfliPNG.\n\n\nRDB archive (.rdb)\n\n    It is an archive format found in Tencent QQ.\n    Leanify all files inside.\n\n\nFlash file (.swf)\n\n    Leanify embedded images.\n    Recompress it with LZMA.\n    Remove Metadata Tag.\n\n\nSVG image (.svg, .svgz)\n\n    It is based on XML.\n    Remove metadata.\n    Shrink spaces in attributes.\n    Remove empty attributes.\n    Remove empty text element and container element.\n\n\ntar archive (.tar)\n\n    Leanify all files inside.\n\n\nXML document (.xml, .xsl, .xslt)\n\n    Remove all comments, unnecessary spaces, tabs, line breaks.\n\n\nXPInstall (.xpi)\n\n    It is based on ZIP.\n    Note that modifying files inside xpi will break digital signature. \n    To install it, you'll have to sign it again.\n\n\nXPS document (.xps, .oxps)\n\n    It is based on XML and ZIP.\n\n\nZIP archive (.zip)\n\n    Leanify all files inside and recompress deflate stream using Zopfli.\n    Use STORE method if DEFLATE makes file larger.\n    Remove extra field in Local file header.\n    Remove Data descriptor structure, write those information to Local file header.\n    Remove extra field and file comment in Central directory file header.\n    Remove comment in End of central directory record.<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To have the packager handle pdf conversions it&#8217;s recommended to be running Windows and having Microsoft Word installed since that creates the best conversions. On Linux Libre Office is used if found otherwise Pdf Gearbox. But I have noticed that layout and missing fonts often are an issue on Linux.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"dlicore_quick_reference\"><\/span>dliCore Quick Reference<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here are some quick references to components and ideas used in dliCore to help you get your plugins done faster.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"hooks\"><\/span>Hooks<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">dliCore plugins support all hooks in Osclass and third party plugins. To have your plugin handle the &#8220;header&#8221; hook add the following function<br>to your myFooPlugin class.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public function headerHook() {\n    \/\/ Code to run\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In the same way you can add a hook to &#8220;user_register_form&#8221; by adding the following function.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public function userRegisterFormHook() {\n    \/\/ Code to run\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that you do not have to do anything more than to create the function to make it work. dliCore will handle the wiring to make sure your function is called when the hook is run.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"docblock_hook_definitions\"><\/span>DocBlock Hook definitions<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Alternative ways of defining hooks using a DocBlock for a function in your Plugin also exists to provide flexibility. You can make your function hook the header hook by adding<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook        header\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can set the priority of the hook<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook        header\n * @priority    5\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can run your function under multiple hooks<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook        header\n * @hook        footer\n * @priority    5\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can make each hook have its own priority<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook        header:6\n * @hook        footer:2\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can also write this on a single line by using comma to separate the hooks<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook        header:6, footer:2\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can set restrictions so that your hook only run under certain situations. Restrictions are the Rewrite objects location or location-section, or a dliCore based Plugins route like PluginName-Controller-Action. Since the homepage contains an empty location and section a special case name of &#8216;index&#8217; is used to refer to that page.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook        header\n * @restriction login\n * @priority    5\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can add multiple restrictions either as separate tags or by using comma to separate them<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook        header\n * @restriction login, item, FooBar-User-edit\n * @priority    5\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see you can create quite elaborate hook rules for you functions.<br>You can also do all of this in a single tag. To make your function run in the header hook on the login and register-register pages with a priority of 8 and under the footer hook at the item page with a priority of 6 you can write<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @restrictedHook  header : login, register-register : 8\n *                  footer : item : 6\n *\/<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each entry should be on a new line. Note that if this is used, any @hook or @restriction tags are ignored. You can also combine things like using the fooBarHook naming convention along with setting the priority in the DocBlock.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If your function is static your Plugin is not instantiated by the hook being called. This can be used for instance if you have some small script or style that should be included on every page load but the entire Plugin is not needed for every page.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/**\n * My function description\n *\n * @hook  header\n *\/\npublic static function enqueuStyles() {\n    osc_enqueue_style('foobar', $this->getPluginUrl() . 'assets\/css\/foobar.css');\n}<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"registering_plugins_own_hooks\"><\/span>Registering Plugins own hooks<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Optionally, you can register hooks your own plugin will call to have them for instance automatically written to the info file during Plugin packaging of the dliDevTools Helper.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To register a hook your Plugin will provide you add the following in your Plugins _init function<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$this->_registerHook(new Hook('AfterMessageSent', 'Called after a message has been sent', ['recipientId' => 'id of receiving user']));<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To trigger the hook within your plugin you can then use<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$this->runHook('AfterMessageSent', 10);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If your Plugin is called myFoo the hook that is actually called will be<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">myfoo_after_message_sent<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In the info file the above hook would add the following entry<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">myfoo_after_message_sent\n\n    Arguments:\n                recipientId\n                id of receiving user<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This helps users of your plugin identify which hooks you provide and how they function.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"controllers\"><\/span>Controllers<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Controllers are used to separate your logic from your presentation.<br>Controllers are placed under the Controllers folder and should end in Controller.php. Action functions in your controller will be wired to respond to specific routes and are where you write your logic for a specific action. Their name should end in Action. Let&#8217;s say you have a user profile part in your plugin myFoo. If you create the following function<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class UserController extends WebSecBaseController\n{\n    public function profileAction() {\n\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">this will auto map the function to respond to the route &#8220;myFoo-User-profile&#8221;. By creating a link using<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;a href=\"&lt;?php echo osc_route_url('myFoo-User-profile')?>\">User Profile&lt;\/a><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">you would have a link that would automatically trigger the profileAction function in your UserController. You do not have to manually create new routes etc. dliCore handle all the wiring for you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a given plugin you can get a registered route by calling<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$fooPlugin->getRoute('User', 'profile');<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The returned route then presents a function getName() which would return &#8220;myFoo-USer-profile&#8221;. Besides this the route also presents additional functions such as getUrl, getAjaxUrl etc.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are three base controllers you can extend your Controllers from<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">BaseController\n    Can be run by anyone. Should be used for public pages.\n\nWebSecController\n    Can only be run by logged in users. Should be used functionality that requires a user to be logged in.\n\nAdminSecBaseController\n    Can only be run by logged in administrators. Should be used for handling the administrative back end.<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To make sure your Plugin knows about your controllers you specify them in a protected member variable of your Plugin class called <code>_controllers<\/code><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class FooBarPlugin extends Plugin\n{\n    protected $_controllers = [ 'FooBar\\Controllers\\UserController',\n                                'FooBar\\Controllers\\AuthenticatedUserController',\n                                'FooBar\\Controllers\\AdminController'];<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This enables the <code>PluginManager <\/code>to know about and register routes to <code>Controllers <\/code>without having to instantiate the <code>Plugin<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"views\"><\/span>Views<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Views are where you write your presentation and should basically contain your HTML code that goes with any given Action in your Controller. Your view files should be placed under <code>views\/{ControllerName}\/{ActionName}.php<\/code> So your view file fo the profileAction of your UserController would reside in<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">views\/User\/profile.php<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Support for theme specific views exist. If you would like to have a specific view file for the FooBar theme you would place your view file in<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">themes\/FooBar\/views\/User\/profile.php<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If a special view file for the active theme is found that will be used. Otherwise the default view located under views\/User\/profile.php<br>will be used as a fallback.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"fragments\"><\/span>Fragments<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Fragments are like a special type of view one might say. I could have used this approach for the entire view model, but since Osclass has it&#8217;s own way of rendering custom files I didn&#8217;t want to break that and instead jack into that for the views. But what if you don&#8217;t have a Controller? Perhaps you don&#8217;t want to or perhaps you wan&#8217;t some piece of HTML to be rendered as part of some hook being run.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is where Fragments come into play. They allow you to take a file to be rendered and pass data to it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s say you have a function to add some form element to the item form when a new item is added using the &#8220;item_form&#8221; hook.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We add the following to our Plugin class.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public function itemFormHook($catId) {\n    if(!$this->isActiveForCategory($catId)) {\n        return;\n    }\n    $fragment = new Fragment($this, 'itemForm.php');\n    $fragment->checked = false;\n    $fragment->render();\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will now run when the <code>item_form<\/code> hook is run and will create a Fragment. The fragment will use the file <code>itemForm.php<\/code> The contents of <code>itemForm.php<\/code> could look like this.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;div class=\"box myFoo_item_form\">\n    &lt;h2>&lt;?php _e(\"My important option\", \"myFoo\")?>&lt;\/h2>\n    &lt;div class=\"control-group\">\n        &lt;div class=\"controls\">\n            &lt;input id=\"myImportantOptionStatus\" type=\"checkbox\" name=\"myImportantOptionStatus\" &lt;?php echo ($this->checked ? 'checked' : '')?>> &lt;label for=\"myImportantOptionStatus\">THis is my important label&lt;\/label>\n        &lt;\/div>\n    &lt;\/div>\n&lt;\/div><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see it&#8217;s pretty much just HTML code. There is one check to see if <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$fragment-&gt;checked<\/code> is true or false. If it&#8217;s true then we make sure that the checkbox is checked. This allows us to use the same fragment when editing<br>an item. Fragments can easily be used inside view files etc.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When you create a Fragment you tell it what base path it should use. You can give either a path or an instance of a Plugin. The Fragment will look for files in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">base path+\"\/fragments\"<\/code> folder. To create a Fragment inside your Plugin that should look for its files in the fragments folder of the plugin you just pass it a reference to $this like we did in the example<br>above.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Support for theme specific fragments exist. If you would like to have a specific fragment file for the FooBar theme you would place<br>your fragment file in<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">themes\/FooBar\/fragments\/itemForm.php<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If a special fragment file for the active theme is found that will be used. Otherwise the default fragment located under <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">fragments\/itemForm.php<\/code> will be used as a fallback.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"tables\"><\/span>Tables<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Most plugins saves stuff to tables in the database. To create a table for your plugin you create a class that extends <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliLib\\Db\\AbstractTable<\/code>. As a convention you can place the class in a sub folder named Tables. This is not a requirement, but I&#8217;m considering adding functionality to auto load tables in the same manner as Controllers and will most likely look for them in this folder if that is the case.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s say our plugin stores some extended profile information about a user that we would like to store in the database. To create a table to handle this the class could look like this.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">namespace myFoo\\Tables;\n\nuse dliLib\\Db\\AbstractTable;\n\nclass ExtendedUserProfileTable extends AbstractTable {\n    protected $_tableName = 't_myfoo_extended_user_profiles';\n\n    protected function _init() {\n        $this->_struct = \"CREATE TABLE IF NOT EXISTS \/*TABLE_NAME*\/ (\n                            fk_i_user_id INT UNSIGNED NOT NULL,\n                            's_favourite_movie' VARCHAR(128) DEFAULT NULL\n                            PRIMARY KEY (fk_i_user_id),\n                            FOREIGN KEY (fk_i_user_id) REFERENCES \/*TABLE_PREFIX*\/t_user (pk_i_id) ON DELETE CASCADE\n                        ) ENGINE=InnoDB DEFAULT CHARACTER SET 'UTF8' COLLATE 'UTF8_GENERAL_CI';\";\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We set the table name, excluding the OC prefix, in the <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$_tableName<\/code> member variable. Then we set the $_struct member variable to hold the create table struct. This should be done in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">_init()<\/code> function so that the new key word <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">\/*<em>TABLE_NAME<\/em>*\/<\/code> is known.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To have the plugin create and drop the table when installed and uninstalled, all you have to do is to register the Table with the Plugin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If we assume you save the table under the Tables folder in the Plugin directory you do the following in the Plugin&#8217;s <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">_init()<\/code> function.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">protected function _init() {\n    \/\/ Register Tables\n    $this->_registerTable('myFoo\\Tables\\ExtendedUserProfileTable', 'ExtendedProfileTable');\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The first parameter points to the class of your Table, including the namespace for it. The second parameter is a friendly name that you give your Table.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, when you plugin is installed the Table will be created and when your Plugin is uninstalled the Table will be removed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Using this functionality is optional in dliCore and you can create your own DAO if wanted.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"models\"><\/span>Models<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To work against your Tables you also have the option to create Database Models. They are essentially objects that you can work with and save etc without manually writing the data of it to the database.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To create a model for our <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">ExtendedUserProfileTable<\/code> above we would write the following class extending <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliLib\\Model\\DbModel<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">namespace myFoo\\Models\\Google;\n\nuse dliLib\\Model\\DbModel;\nclass ExtendedUserProfile extends DbModel\n{\n\/\/ Tell the model which table it is connected to\nprotected static $_dbTableClass = 'myFoo\\Tables\\ExtendedUserProfileTable';\n\n\/\/ Member variables to represent each column name. Note that prefixes such as fk_, s_, i_ etc are removed and\n\/\/ the column name converted to camelCase with a prefixing _.\nprotected $_userId = null;\nprotected $_favouriteMovie = null;\nprotected $_favouriteQuote = null;\n\n\/**\n* @return the user id\n*\/\npublic function getUserId()\n{\n    return $this->_userId;\n}\n\n\/**\n* @param int $userId\n*\/\npublic function setUserId($userId)\n{\n    $this->_userId = (int)$userId;\n}\n\n\/**\n* @return the favourite movie\n*\/\npublic function getFavouriteMovie()\n{\n    return $this->_favouriteMovie;\n}\n\n\/**\n* @param string $favouriteMovie\n*\/\npublic function setFavouriteMovie($favouriteMovie)\n{\n    $this->_favouriteMovie = $favouriteMovie;\n}\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see we just supply simple getter and setter functions for our internal variables. In some instances setting a variable could involve more logic and bounds checking for instance. <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliLib\\Model\\DbModel<\/code> handles the wiring for you and your model is now ready to be used.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Get current users ExtendedUserProfile\n\/\/ find() works on the primary key which in this case was the foreign key to the user.\n$extendedUserProfile = ExtendedUserProfile::find(osc_current_logged_user_id());\n\n\/\/ Make sure this user actually had an extended profile\nif($extendedUserProfile) {\n    echo \"Favourite Movie is \" . $extendedUserProfile->getFavouriteMovie() . \"&lt;br\/>\";\n\n    \/\/ Update and save data in the model\n    $extendedUserProfile->setFavouriteMovie(\"Robin Hood\");\n    $extendedUserProfile->save();\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To delete a model you call the static function delete and pass the instance of the model to delete. After the model has been deleted the model object will be <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">null<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ExtendedUserProfile::delete($extendedUserProfile);\n\n\/\/ $extendedUserProfile is now null<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Using this functionality, like almost every single mechanism, is optional in dliCore and you can create your own DAO if wanted.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"automatic_caching_of_model_objects\"><\/span>Automatic caching of model objects<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">dliCore offers a sophisticated automatic caching mechanism (if enabled by the site operator) when models are used. This system automatically keeps track of changes in an object to invalidate the cached object and also invalidates any cache of multiple models that the object was part of.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For instance, say you select a given object with<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$foo = FooModel::find(1);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This fetches the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">FooModel<\/code> with id <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">1<\/code> into <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$foo<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then you might also retreive this object as part of a collection like<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$foos = FooModel::findAll();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, the instance of <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$foo<\/code> with <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">id = 1<\/code> within<code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\"> $foo<\/code> and <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$foos<\/code> will be the same. And if you run<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$foo->setName('Bar');\n$foo->save();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The cache containing the individual <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">FooModel<\/code> object as well as the collection created when finding all are both invalidated<br>since an object that is part of the caches has changed.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"avoiding_caching_of_certain_properties_of_an_object\"><\/span>Avoiding caching of certain properties of an object<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Sometimes you might not want every property of an object to be cached. One such time can be when you fetch a collection of other model objects that might &#8220;belong&#8221; to the main object. But you would not like these objects to be cached inside the main object but instead cached as their own entities to avoid out of sync data between the main object containing the &#8220;child&#8221; objects and the actual child objects caches. Since the data cached in the main object is cached when the main object is cached and as part of that object. Changing the child object does not invalidate the main objects cache and thus the main object will contain out of sync data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To avoid this you could easily not store the objects in the main object at all but instead fetch them when needed. For example in <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">FooModel <\/code>we could have a function<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public function getChildObjects() {\n    return BarModel::_fetchAll(BarModel::getDbTable()->selectConditions()->where('fk_i_parent_id', $this->_id));\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, the objects are retrieved using the model system, cached and invalidated as normal and will be &#8220;correct&#8221;. Because of the way models are handled and cached it will also not actually be fetched each time from the database but instead already fetched data will be returned if it exists.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But sometimes you might want to keep an array of the child objects within the parent object. Thankfully dliCore provides a way to achieve this and not run into the above mentioned caching inconsistency issue.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s say you have the following functions in you FooModel<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ After load we load all the child objects abd place them into an array within the parent\nprotected function _afterLoad() {\n    $this->_children = BarModel::_fetchAll(BarModel::getDbTable()->selectConditions()->where('fk_i_parent_id', $this->_id));\n}\n\n\/\/ Before we delete the parent we delete all the children of this parent\nprotected function _beforeDelete() {\n    BarModel::deleteWhere(['fk_i_pranet_id' => $this->_id]);\n}\n\n\/\/ After we save the parent we set a field in the child referensing this parent and save all the children\nprotected function _afterSave() {\n    \/\/ Save all the recipients\n    foreach($this->_children as $child) {\n        $child->setParent($this);\n    }\n    BarModel::saveBatch($this->_children);\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, to avoid this array we are working with to be part of a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">FooModel <\/code>object that is cached we simply add the following tag to the array in the class.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">    \/**\n     * @var FooModel[]\n     * @nocache\n     *\/\n    protected $_children;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This @nocache tag will tell the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">CacheSystem <\/code>to ignore this specific member of the class when caching the data. So all of the data of the <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">FooModel <\/code>will be cached, except the children we hold in <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">$_children<\/code>. They will be cached by their own model when loaded in the <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">BarModel::_fetchAll<\/code> call instead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The next time our <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">FooModel <\/code>is fetched from cache <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">_afterLoad<\/code> will be called, the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">BarModel <\/code>children fetched, which actually<br>means they will be fetched from cache and then they will once again exist within our <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">FooModel <\/code>object now fetched from the<br>cache.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"emails\"><\/span>Emails<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Creating emails to send in Osclass usually involves adding them to the pages table in the database etc so that they are available for translation. This to is simplified using dliCore. <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Emails <\/code>work much like <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Tables<\/code>. You create a class that extends <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">dliLib\\Email\\AbstractEmail<\/code>. Let&#8217;s say we wanted to be able to notify a user if someone thought his\/her selected favorite move was boring.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?php\nnamespace myFoo\\Emails;\n\nuse dliLib\\Email\\AbstractEmail;\nclass BoringFavouriteMovie extends AbstractEmail\n{\n    \/**\n     * Should return the internal Osclass description of the email.\n     * This helps you identify it when editing and translating emails.\n     *\n     * @return string\n     *\/\n    protected function _getDescription()\n    {\n        return 'myFoo - Boring Movie';\n    }\n\n    \/**\n     * Should return the default title\/subject of the email\n     *\n     * @return string\n     *\/\n    protected function _getDefaultTitle()\n    {\n        return '{WEB_TITLE} - Your selected favourite movie is boring';\n    }\n\n    \/**\n     * Should return the default content\/body of the email\n     *\n     * @return string\n     *\/\n    protected function _getDefaultContent()\n    {\n        return \"&lt;p>Hi {USER_NAME}!&lt;\/p>\\r\\n&lt;p> &lt;\/p>\\r\\n&lt;p>A fellow user has indicated that your selected favourite movie {MOVIE_TITLE} is very boring. Consider watching better movies.&lt;\/p>\\r\\n&lt;p>This is an automatic email, Please do not respond to this email.&lt;\/p>\\r\\n&lt;p> &lt;\/p>\\r\\n&lt;p>Thanks&lt;\/p>\\r\\n&lt;p>{WEB_TITLE}&lt;\/p>\";\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that you can add any number of dynamic tags here enclosed in <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">{}<\/code> such as <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">{MOVIE_TITLE}<\/code>. Then we register the email with the plugin we add it to the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Plugins<\/code> <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">_init()<\/code> function.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">protected function _init() {\n    \/\/ Register tables\n    $this->_registerTable('myFoo\\Tables\\ExtendedUserProfileTable', 'ExtendedProfileTable');\n\n    \/\/ Register emails\n    $this->_registerEmail('myFoo\\Emails\\BoringFavouriteMovie', 'BoringFavouriteMovie');\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The email will now be added to the database when you install your plugin and removed once your plugin is uninstalled. It can be edited like normal in the Osclass admin under <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Settings\/Email templates<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To send a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">BoringFavouriteMovie <\/code>mail you create an instance of it and pass an array with values for the dynamic tags.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$email = new BoringFavouriteMovie(['{MOVIE_TITLE}' => $boringMovieName]);\n$email->addTo($userEmailAddress, $userName);\n$email->send();<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"dependencies\"><\/span>Dependencies<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Having plugins dependent on other plugins can be a hassle to handle. <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore<\/code> itself becomes a dependency for any plugin built using it. Luckily <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore <\/code>has support for handling <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dependencies <\/code>of Plugins as well.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First of all, the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">index.php<\/code> that is generated by the CLI tool <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliHelper.php<\/code> in the bin folder sets up functions that make sure <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore <\/code>exists and explains to any user trying to install the plugin that they need <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore <\/code>and where they can download it if they don&#8217;t have it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If your plugin depends on a particular version of <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore <\/code>or any other Osclass plugin you can let the system know that to.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In your Plugin&#8217;s <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">_init()<\/code> function you could add<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">protected function _init() {\n    \/\/ We depend on having at least version 0.1.5 of dliCore\n    $this->_registerDependency(new PluginDependency('dliCore', '0.1.5'));\n\n    \/\/ We require Osclass to be at least version 2.3.0\n    $this->_registerDependency(new OsclassDependency('2.3.0'));\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There are several possible dependency types to register for your Plugin. Examples include<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">MysqlDependency\nOsclassDependency\nPhpDependency\nPluginDependency<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With them you can set min and max versions of given dependencies that your Plugin requires. During installation the Plugin will check all of the registered dependencies and report if anyone is not fulfilled. Some dependency types have extra features, like for instance the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">PluginDependency<\/code> where you can set a download url to present to the user so they get a link to where they can download the missing dependency etc.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"optional_dependencies\"><\/span>Optional Dependencies<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Some functionality of your plugin might depend on some external dependency, but your plugin does not require it to function. Let&#8217;s for instance say that you have a plugin that will have to act differently depending on if you have version <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">1<\/code> or <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">2<\/code> of a plugin called <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Foo <\/code>installed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To accomplish this you<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$this->_registerDependency(\n        (new PluginDependency('Foo', '1.0.0'))->\n        setAsOptional('Without PHP you will not be able to run any PHP code'))\n    );<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"processes\"><\/span>Processes<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">dliLib\\Shell\\Process<\/code> component allows you to execute processes. You can select to execute a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Process <\/code>and wait for it to finish<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$process = new Process('ping', ['-n' => 5, 'www.google.com']);\n$process->executeAndWait();\necho $process->getStdOut();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">or you can select to run the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Process <\/code>asynchronously to have the Process run in the background. This is good for long running Processes.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$process = new Process('ping', ['-n' => 5, 'www.google.com']);\n$uid = $process->execute();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When running a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Process <\/code>asynchronously you can set a timeout that will kill the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Process <\/code>if it has been running for longer than this.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Kill Process if it has been running for longer than 120 seconds\n$process->setTimeOut(120);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When running <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Processes <\/code>asynchronously you can even <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">re-attach<\/code> to it in later requests to check it&#8217;s status and get output.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$process = ProcessManager::getInstance()->getProcessByUid($uid);\n\n\/\/ Check if process is still running\nif($process->isRunning()) {\n    \/\/ Do something\n}\n\n\/\/ Get output\necho $process->getStdOut();<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To facilitate this, Process information, stdout and stderr are stored in the filesystem. This could also have been achieved by some shared memory space or similar. But since that can be turned off, and often is in shared hosting for instance, this approach was selected to makes sure it works for all users without having to have access to and change configurations. It is easy to select to store this in a ram disk for instance to avoid writing to disk. The <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">ProcessManager <\/code>is responsible for cleaning this up after a <code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">Process <\/code>has completed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"jobs\"><\/span>Jobs<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Jobs <\/code>are an extension of <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Processes<\/code>. Think of them as cron jobs. Tasks that are recurring at specific intervals. You could keep an eye on this yourself and on every page load check if some task should be carried out or not.<br>The downside is that if no page loads are done, then you check will never run. Also, running the checks on every page load could affect the perceived performance of your site.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Before, you solved this by adding an entry to a script in your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">crontab <\/code>or <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Windows Task Scheduler<\/code>. The downside to that is that it requires the end user of your Plugin to have access to and be able to configure those parts of the system. For some people it&#8217;s just hard while for others, in some shared hosting environments for instance, it&#8217;s actually impossible<br>since they might not have access to their crontab.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Jobs <\/code>solves all of these issues by adding a Cron-like manager system to Osclass itself. It will not check your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Jobs <\/code>every page load but instead start an instance of the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">JobManager<\/code> in its own <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Process <\/code>and have that check and execute your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Jobs <\/code>for you. This results in Jobs that can execute in parallel with the normal operations of your site without affecting the performance of your site in the same way checking and running tasks when a user<br>loaded a page would.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This also has the benefit that you no longer need to ask your users to edit the crontab to enable your Plugin to run required recurring work. You can just have your Plugin register a Job when it&#8217;s installed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s say we have a Plugin that generates a sitemap used by search engines. To do this it has to run a task to generate the sitemap once every hour. Before, the user would install your Plugin and then to have the sitemap be generated you could do one of two things. Either ask the user to enter a given command in the crontab<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">0 * * * * php \/var\/html\/osclass\/oc-content\/plugins\/mySitemapPlugin\/generateSitemap.php<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This would require the user to have access to their crontab or figure out how to schedule this task in their <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Windows Task Shceduler<\/code>. It would also require a second step from the users after installing your Plugin before they could actually start to use it. Not very user friendly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The second solution would be to check if it was time to generate the sitemap on every page load. If it was, the function that generated the sitemap would be run. This would result in some page loads taking a long time since it would have to wait for the sitemaps generation to finish before returning the content to the users browser.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore <\/code>and the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">JobManager<\/code> you could just add the following to your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">installHook()<\/code> function of your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Plugin<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$job = new ShellCommandJob('php -f ' . $this->getPluginPath().'generateSitemap.php');\n$job->setName('Sitemap Generation');\n$job->setSchedule('@hourly');\n$this->_registerJob($job);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">generateSitemap.php<\/code> script will be run once per hour. If your plugin is uninstalled the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Job <\/code>will be unregistered automatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note here that we execute <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">php<\/code> and pass in our script using a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">ShellCommandJob<\/code>. This works, but <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">ShellCommandJob <\/code>was mostly<br>intended to run other command line commands. If we want to run php code we could instead use a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Closure<\/code>. This is supported through the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">ClosureJob<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$fn = function() {\n    \/\/ Code to generate sitemap\n};\n\n$job = new ClosureJob($fn);\n$job->setName('Sitemap Generation');\n$job->setSchedule('@hourly');\n$this->_registerJob($job);<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here we create an anonymous function and register that with the job. This function will now be called once every hour. To run scripts you could also register it as a route and execute that route. Let&#8217;s assume you have a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">SitemapController <\/code>that is registered with you <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Plugin <\/code>and it contains a function <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">generateSitemapAction<\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class SitemapController extends BaseController\n{\n    ...\n\n    public function generateSitemapAction() {\n        \/\/ Code to generate sitemap\n    }\n\n    ...\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You would now have a route available to your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">generateSitemapAction<\/code>. So now we could register a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Job <\/code>to execute this given route.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$job = new RouteJob($this->getName().'-Sitemap-generateSitemap');\n$job->setName('Sitemap Generation');\n$job->setSchedule('@hourly');\n$this->_registerJob($job);<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"plugin_storage\"><\/span>Plugin storage<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Sometimes your plugin might need to store data. This could be data that should be <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">private <\/code>or <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">public <\/code>data like a generated xml file. To allow for easy handling of such storage <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore <\/code>offers several functions to get paths to location to store <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">temporary<\/code>, <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">private <\/code>and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">public <\/code>data. If there is a desire to change the default values of these locations that can be done in the Admin interface under <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore\/Settings<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All files and folders in the plugins data directories are removed when the plugin is uninstalled.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"plugin_storage_functions\"><\/span>Plugin storage functions<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">getTempDir($path)<\/code><br>Returns the path to the Plugins temp directory and an optional path within the temp dir. If the path does not exist it will be created.<br>Temporary files are not to be considered persistent and will be removed at the discretion of the server. Most often at reboot.<br>All files and folders within the Plugins temp dir will be deleted when the plugin is uninstalled!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">getPrivateStorageDir($path)<\/code><br>Returns the path to the Plugins private storage directory and an optional path within the temp dir. If the path does not exist it will be created.<br>All files and folders within the Plugins private storage directory will be deleted when the plugin is uninstalled! This folder should point to a folder that the client canb&#8217;t access. So for instance, the web browser should not be able to access this folder. If possible this should be configured to point to a directory outside of the public html folder.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">getPublicStorageDir($path)<\/code><br>Returns the path to the Plugins public storage directory and an optional path within the temp dir. If the path does not exist it will be created.<br>All files and folders within the Plugins public storage directory will be deleted when the plugin is uninstalled! Plugins should use the public storage directory to store files that should be publicly accessible for instance dynamic data, xml files, images etc.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">getPublicStorageUrl($path)<\/code><br>Returns url to existing public storage directory<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"plugin_storage_related_hooks\"><\/span>Plugin storage related Hooks<span class=\"ez-toc-section-end\"><\/span><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">When reconfigured under Settings in dliCore Admin hooks are fired to give plugin developers the option to react to the change. The developer will not have to manage moving files, but for instance, Processes that are locking files in that folder might have to be terminated before the move etc.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dlicore_pre_plugins_temp_base_directory_changed\n\nDescription:\nCalled before moving existing plugin files when the base directory for temporary files are changed\n\nArguments:\n\n    oldPath\n     String containing old path\n\n    newPath\n     String containing new path<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dlicore_pre_plugins_private_base_directory_changed\n\nDescription:\nCalled before moving existing plugin files when the base directory for plugins non public files are changed\n\nArguments:\n\n    oldPath\n     String containing old path\n\n    newPath\n     String containing new path<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dlicore_pre_plugins_public_base_directory_changed\n\nDescription:\nCalled before moving existing plugin files when the base directory for plugins public files are changed\n\nArguments:\n\n    oldPath\n     String containing old path\n\n    newPath\n     String containing new path<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dlicore_post_plugins_temp_base_directory_changed\n\nDescription:\nCalled after moving existing plugin files when the base directory for temporary files are changed\n\nArguments:\n\n    oldPath\n     String containing old path\n\n    newPath\n     String containing new path<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dlicore_post_plugins_private_base_directory_changed\n\nDescription:\nCalled after moving existing plugin files when the base directory for plugins non public files are changed\n\nArguments:\n\n    oldPath\n     String containing old path\n\n    newPath\n     String containing new path<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dlicore_post_plugins_public_base_directory_changed\n\nDescription:\nCalled after moving existing plugin files when the base directory for plugins public files are changed\n\nArguments:\n\n    oldPath\n     String containing old path\n\n    newPath\n     String containing new path<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"additional_components\"><\/span>Additional Components<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There are many more components available in <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dliCore <\/code>such as a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Caching Manager<\/code>, <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Forms <\/code>and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Html element wrappers<\/code>, <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Html Widgets<\/code>, <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">FileManager<\/code>, <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">menu builders<\/code> etc.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But the ones mentioned above are the most fundamental. I&#8217;m adding new, refining and re-writing existing components all the time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ideas and questions can be directed to me.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"docker_development_image\"><\/span>Docker Development Image<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To quickly enable local development of plugins I have created a Docker image with PHP and MariaDB.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note that it is NOT intended for production use, only local development. It allows you to test against multiple versions of PHP including<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Version | Protocol | Port<br>===========================<br>5.3.29 | http | 8053<br>5.3.29 | https | 8153<br>5.4.44 | http | 8054<br>5.4.44 | https | 8154<br>5.5.38 | http | 8055<br>5.5.38 | https | 8155<br>5.6.30 | http | 8056<br>5.6.30 | https | 8156<br>7.0.17 | http | 8070<br>7.0.17 | https | 8170<br>7.1.3 | http | 8071<br>7.1.3 | https | 8171<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All versions have X-debug installed for debugging and PHP 5.6 also comes with Z-Ray configured so you can profile your application.<br>If running windows you can place the bellow script into a file like rundevenv.bat and place it in the root of your project.<br><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">DEV_CLIENT_IP <\/code>and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">DEV_CLIENT_PORT <\/code>should be the LAN ip of your computer and the port your IDE is listening to debug connections<br>on.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">@ECHO OFF\nSET DEV_CLIENT_IP=192.168.0.111\nSET DEV_CLIENT_PORT=9000\n\nPowerShell.exe -Command \"&amp; docker run --rm -t -i -v $PWD\\:\/var\/www:rw -v $PWD\/dbdata:\/var\/lib\/mysql -v $PWD\/etc\/mysql:\/etc\/mysql -v $PWD\/etc\/apache2:\/etc\/apache2 -e DEV_CLIENT_IP=\"%DEV_CLIENT_IP%\" -e DEV_CLIENT_PORT=%DEV_CLIENT_PORT% -e MYSQL_ROOT_PASSWORD=\"root\" -p 8051:8051 -p 8052:8052 -p 8053:8053 -p 8054:8054 -p 8055:8055 -p 8056:8056 -p 8070:8070 -p 8071:8071 -p 8151:8151 -p 8152:8152 -p 8153:8153 -p 8154:8154 -p 8155:8155 -p 8156:8156 -p 8170:8170 -p 8171:8171 -p 3306:3306 inquam\/phpdev\"\nPAUSE<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When you run this script an apache web server will start with the different PHP versions on the specified ports, MariaDB will also run. <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">\/etc\/mysql<\/code> and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">\/etc\/apache2<\/code> will be created in your project root to allow you to edit config files and the database files will be stored in a folder named dbdata under your project root.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note that you must have Docker installed before running this command.<br>To shut down the image just press <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">ctrl-c.<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Edit your <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">config.php<\/code> to include these directives in order to work with this environment<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/** MySQL hostname *\/\ndefine('DB_HOST', 'localhost');\n\n\/** Database Table prefix *\/\ndefine('DB_TABLE_PREFIX', 'oc_');\n\ndefine('REL_WEB_URL', '\/');\n\ndefine('WEB_PATH', (isset($_SERVER['HTTPS']) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &amp;&amp; strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https' ? \"https\" : \"http\"). ':\/\/localhost:' . (!empty($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : '8056') . '\/');<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>This is a copy of the README that comes with dliHelper and dliCore CLI tool In \/bin you will find a script dliHelper.php that can help you to get started even faster. Note that this tool is just started and is subject to change \ud83d\ude42 Usage: dliHelper.php &#8211;method=&#8221;methodName&#8221; [args] &#8211;method help createPlugin&#8211;internalName: Used for namespace,&hellip;&nbsp;<a href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/\" rel=\"bookmark\">Read More &raquo;<span class=\"screen-reader-text\">dliCore<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":256,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","neve_meta_reading_time":"","footnotes":""},"class_list":["post-461","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>dliCore - Daniel Liljeberg<\/title>\n<meta name=\"description\" content=\"Quick reference for dliCore library to build plugins for Osclass\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/\" \/>\n<meta property=\"og:locale\" content=\"sv_SE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"dliCore - Daniel Liljeberg\" \/>\n<meta property=\"og:description\" content=\"Quick reference for dliCore library to build plugins for Osclass\" \/>\n<meta property=\"og:url\" content=\"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/\" \/>\n<meta property=\"og:site_name\" content=\"Daniel Liljeberg\" \/>\n<meta property=\"article:modified_time\" content=\"2021-01-15T13:27:26+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Ber\u00e4knad l\u00e4stid\" \/>\n\t<meta name=\"twitter:data1\" content=\"29 minuter\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/my-portfolio\\\/software\\\/dlicore-2\\\/\",\"url\":\"https:\\\/\\\/daniel.liljeberg.io\\\/my-portfolio\\\/software\\\/dlicore-2\\\/\",\"name\":\"dliCore - Daniel Liljeberg\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/#website\"},\"datePublished\":\"2021-01-13T11:43:45+00:00\",\"dateModified\":\"2021-01-15T13:27:26+00:00\",\"description\":\"Quick reference for dliCore library to build plugins for Osclass\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/my-portfolio\\\/software\\\/dlicore-2\\\/#breadcrumb\"},\"inLanguage\":\"sv-SE\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/daniel.liljeberg.io\\\/my-portfolio\\\/software\\\/dlicore-2\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/my-portfolio\\\/software\\\/dlicore-2\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/daniel.liljeberg.io\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Portfolio\",\"item\":\"https:\\\/\\\/daniel.liljeberg.io\\\/my-portfolio\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/daniel.liljeberg.io\\\/my-portfolio\\\/software\\\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"dliCore\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/#website\",\"url\":\"https:\\\/\\\/daniel.liljeberg.io\\\/\",\"name\":\"Daniel Liljeberg\",\"description\":\"The is no place like 127.0.0.1\",\"publisher\":{\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/#\\\/schema\\\/person\\\/e2c3fe10971c37cff2669f5688834cd7\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/daniel.liljeberg.io\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"sv-SE\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/#\\\/schema\\\/person\\\/e2c3fe10971c37cff2669f5688834cd7\",\"name\":\"Daniel Liljeberg\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"sv-SE\",\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/DanielLiljeberg.png\",\"url\":\"https:\\\/\\\/daniel.liljeberg.io\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/DanielLiljeberg.png\",\"contentUrl\":\"https:\\\/\\\/daniel.liljeberg.io\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/DanielLiljeberg.png\",\"width\":424,\"height\":440,\"caption\":\"Daniel Liljeberg\"},\"logo\":{\"@id\":\"https:\\\/\\\/daniel.liljeberg.io\\\/wp-content\\\/uploads\\\/2020\\\/12\\\/DanielLiljeberg.png\"},\"description\":\"Agile practitioner and advocate. Strong believer in the future of agile organizations, businesses and teams. Got my first computer, a C64, at age 7 and computers has been part of my life since then. Working professionally with development since the early 2000\u2019s in a vast array of technologies and roles. Social, easy going, fun loving guy with an appetite for new challenges and new knowledge who has been \u201cthere\u201d and done \u201cthat\u201d. That\u2019s a good way to sum it all up. Married and father of three kids. All true blessings ;)\",\"sameAs\":[\"https:\\\/\\\/daniel.liljeberg.io\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/danielliljeberg\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"dliCore - Daniel Liljeberg","description":"Quick reference for dliCore library to build plugins for Osclass","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/","og_locale":"sv_SE","og_type":"article","og_title":"dliCore - Daniel Liljeberg","og_description":"Quick reference for dliCore library to build plugins for Osclass","og_url":"https:\/\/daniel.liljeberg.io\/sv\/min-portfolio\/mjukvara\/dlicore-2\/","og_site_name":"Daniel Liljeberg","article_modified_time":"2021-01-15T13:27:26+00:00","twitter_card":"summary_large_image","twitter_misc":{"Ber\u00e4knad l\u00e4stid":"29 minuter"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/daniel.liljeberg.io\/my-portfolio\/software\/dlicore-2\/","url":"https:\/\/daniel.liljeberg.io\/my-portfolio\/software\/dlicore-2\/","name":"dliCore - Daniel Liljeberg","isPartOf":{"@id":"https:\/\/daniel.liljeberg.io\/#website"},"datePublished":"2021-01-13T11:43:45+00:00","dateModified":"2021-01-15T13:27:26+00:00","description":"Quick reference for dliCore library to build plugins for Osclass","breadcrumb":{"@id":"https:\/\/daniel.liljeberg.io\/my-portfolio\/software\/dlicore-2\/#breadcrumb"},"inLanguage":"sv-SE","potentialAction":[{"@type":"ReadAction","target":["https:\/\/daniel.liljeberg.io\/my-portfolio\/software\/dlicore-2\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/daniel.liljeberg.io\/my-portfolio\/software\/dlicore-2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/daniel.liljeberg.io\/"},{"@type":"ListItem","position":2,"name":"Portfolio","item":"https:\/\/daniel.liljeberg.io\/my-portfolio\/"},{"@type":"ListItem","position":3,"name":"Software","item":"https:\/\/daniel.liljeberg.io\/my-portfolio\/software\/"},{"@type":"ListItem","position":4,"name":"dliCore"}]},{"@type":"WebSite","@id":"https:\/\/daniel.liljeberg.io\/#website","url":"https:\/\/daniel.liljeberg.io\/","name":"Daniel Liljeberg","description":"The is no place like 127.0.0.1","publisher":{"@id":"https:\/\/daniel.liljeberg.io\/#\/schema\/person\/e2c3fe10971c37cff2669f5688834cd7"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/daniel.liljeberg.io\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"sv-SE"},{"@type":["Person","Organization"],"@id":"https:\/\/daniel.liljeberg.io\/#\/schema\/person\/e2c3fe10971c37cff2669f5688834cd7","name":"Daniel Liljeberg","image":{"@type":"ImageObject","inLanguage":"sv-SE","@id":"https:\/\/daniel.liljeberg.io\/wp-content\/uploads\/2020\/12\/DanielLiljeberg.png","url":"https:\/\/daniel.liljeberg.io\/wp-content\/uploads\/2020\/12\/DanielLiljeberg.png","contentUrl":"https:\/\/daniel.liljeberg.io\/wp-content\/uploads\/2020\/12\/DanielLiljeberg.png","width":424,"height":440,"caption":"Daniel Liljeberg"},"logo":{"@id":"https:\/\/daniel.liljeberg.io\/wp-content\/uploads\/2020\/12\/DanielLiljeberg.png"},"description":"Agile practitioner and advocate. Strong believer in the future of agile organizations, businesses and teams. Got my first computer, a C64, at age 7 and computers has been part of my life since then. Working professionally with development since the early 2000\u2019s in a vast array of technologies and roles. Social, easy going, fun loving guy with an appetite for new challenges and new knowledge who has been \u201cthere\u201d and done \u201cthat\u201d. That\u2019s a good way to sum it all up. Married and father of three kids. All true blessings ;)","sameAs":["https:\/\/daniel.liljeberg.io","https:\/\/www.linkedin.com\/in\/danielliljeberg\/"]}]}},"_links":{"self":[{"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/pages\/461","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/comments?post=461"}],"version-history":[{"count":9,"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/pages\/461\/revisions"}],"predecessor-version":[{"id":484,"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/pages\/461\/revisions\/484"}],"up":[{"embeddable":true,"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/pages\/256"}],"wp:attachment":[{"href":"https:\/\/daniel.liljeberg.io\/sv\/wp-json\/wp\/v2\/media?parent=461"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}