Hooks Question

Ok, let’s look at the language:


[quote]

CH or hook is a specially defined part in the program code that can transfer control to an addon. Hook is declared in advance by calling a special function in the necessary part of code - fn_set_hook(‘hook_name’, $params, [$param2], [$paramN]);.



Example 1. 1

fn_set_hook('get_gift_certificate_info', $_certificate, $certificate, $type)

[/quote]



What do they mean by “necessary part of the code” ?? Now I am trying to figure out how to go in reverse from where fn_set_hook(‘update_category’, …) is called inside of the fn_update_category () function, and see the logic behind why the place where it is called is the necessary part of the code.



And then once it is called, which update function to does it go to and why. I see how the parameters work now I think…

The whole point of the addon structure is that you shouldn’t have to change the code base. Addons should be self-contained. The hooks are pre-defined by cs-cart, and by knowing where those hooks are, you can hook your code into the different functions of cs-cart WITHOUT modifying the base code.

However, for this to work, they need to give us a list of hooks, where they are, and what args they take. If we’re going to develop addons, we can’t be expected to be rooting around in their entire code base.

When I write a facebook app, I get control passed to my program at discrete points, I don’t go looking through all of facebook’s code. This should work the same way. What cs-cart is doing, I assume, is supposedly giving us an API to develop with. On the surface, it seems pretty good - they’ve got their own database abstraction, they use smarty, etc. However, without documentation, it’s pretty useless.

Now, maybe I don’t get what’s going on, and I’d love it if someone would chime in and tell me why. If I do understand, then where is the documentation?

well your addon would be self contained in theory. it would be in its own addon folder with addon.xml and that would tell cs-cart to include that directory in the compiling of the site and php includes at certain points.



I don’t know anything past that. I hope you get some responses because I am interested as well.

This is the best thread I’ve been able to find on the topic. Nice work so far. I also have to figure out the same thing. Have you guys had any more luck since the last post?



Unique, you said you were researching the how the database works? That touches on another requirement I have to meet. Specifically, I have to associate a product with a set of available dates. I think a new table may be required for this. Can you recommended threads related to this?



Thanks,

Timothy

Ok, I think I have found something valuable. If you look at the docs.cs-cart site here:



[url]http://docs.cs-cart.com/addons-directory[/url]



You will notice that the way CS-Cart prefers you make additions to its architecture is by making “addons” which require several things. Most important I believe are an addon.xml, init.php and a func.php. These files together allow the architecture of CS-Cart to be changed or interrupted by an addon when you request it to be done so.



From re-reading these docs over and over it seems that cs-cart places a lot of weight on a directory structure that it is forcing you to adhere to. What I think I have discovered (and anyone correct me if I am wrong) is that you have to place your addon in a new directory in the “addons” directory which can be found int he root of the cs-cart installation.



If you take a look at any ‘addon’ that has been included in the new versions you will see they are all in the addons directory and they all contain at least addon.xml, init.php, and func.php and then some other folders that contain controllers etc.



It is my understanding that cs-cart parses these directories in the addons folder for new directories when you go to the admin panel under Administration->Addons. When it finds a new directory it looks for the addon.xml which is basically an install file that allows cs-cart to make a pointer to the rest of your code in that directory. This part is for you frednurk… CS-Cart has code in almost every major php and smarty file containing a call to the site Registry:: which is populated with all of the hooks that have been registered. I believe that if you register the hooks in the manner that CS-Cart requires you to, your addon code will be successfully included in the code execution on your site.



Instead of trying to figure out how each and every file in the cs-cart root directory calls for addons and then trying to put your addon in the right one, instead, I suggest you create an addon in cs-cart’s way and see how it goes.



So create a directory in the addons folder and create an addon.xml and you will see it show up in your Administration->Addons page. Once you install from there, your code in that directory is referenced and included for later use.



Even more for you frednurk…



You asked before where do you register the hooks. It looks like you register the hooks in the specific init.php file which you have to include in the same directory structure as your addon just like below:


if ( !defined('AREA') ) { die('Access denied'); }

fn_register_hooks(
'your_hook_name',
);




Now once you install ‘your_hook_name’ will be valid hook in cs-cart to be used elsewhere to make a CH (code hook) from the docs.cs-cart site using the fn_set_hook() function of course… So the preliminary answer to your question “where are all the hooks stored?” they are scattered throughout the specific init.php files in the root of every directory in the addons directory. More clearly, every addon has this directory structure:



/addons/your_addon_name/addon.xml

/addons/your_addon_name/init.php

/addons/your_addon_name/func.php



Lastly, the func.php has specific php code to your addon. The code in these func.php files contains code that is comparable to the code in the /core/ directory.



From CS-Cart:



addon.xml

File describes main addon data that is necessary to install and delete the addon.

func.php

Functions used by the addon controllers.

init.php

Primary function – registration of a list of hooks that will be used by the addon.

config.php

Configuration data of the addon.

/controllers

Directory contains files of the addon controllers and also files of pre- and postcontrollers of the program standard controllers.

/schemas

Directory with files extending standard schemas of the program.



For the /controllers/ folder you will notice one more requirement CS-Cart is giving you. You have to do pre- or post- execution of your code… this means basically you are required to stick to another naming convention because of how the cs-cart parser parses the addons directory. Basically, if your code hook will modify how any of the files in the /controllers/ directory functions, then you have to decide whether you want your php code to be executed BEFORE (pre-) or AFTER (post) all of the code in that specific controller is executed. For instance, if you wanted to have your code executed after the products.php file in the controllers directory then you would make a file



/addons/your_addon_name/controllers/products.post.php



I hope this helps us getting down this path of very little documentation that cs-cart has left for us. Fred and everyone else, please try to stay with me on this so we can work together to figure this out. If anything I say doesn’t make sense… well its because it is 5:40 am here and I am struggling to figure all of this out. Hit me back with any questions or constructive criticism.



oh, and always make a backup of everything… I don’t want to mess anyone’s site up. :slight_smile:



Thanks

Unique2,



Your post is correct and this is how it works. Installing the addon through the admin panel it will create the addon in the skins(your skin)\addon(your addon) directory. Disabling the skin doesnt remove the directories it created, it just removes the hook to it.

Unique2 - hehe I just noticed the irony of your name. Nice!



Alright, I’ve read your post and the doc and now some of the code. I think I’m starting to understand the mechanics of hooking, but there are some areas that still don’t make total sense.



First, I agree with your explanation of the fn_register_hooks() function. I see this being called from the init.php file of every addon. For example the tags addon has the following code in the init.php file.


fn_register_hooks(
'delete_product',
'clone_product',
'update_product',
'update_page',
'delete_page',
'clone_page',
'get_additional_product_data',
'get_page_data',
'get_pages',
'get_products',
'get_users',
'seo_is_indexed_page'
);




Essentially, the above code will register, or associate, each of the specified hooks (delete_product, clone_product, update_product, etc) with the current addon, which in this case is the tags addon.



I went ahead and dug into the guts of this function and it’s pretty interesting. Basically, for every unique hook passed in as a parameter, an element is added to the Registry->$hooks array. The element added is an array of arrays. The first element corresponds to the first addon to register a particular hook, the second element corresponds to the second addon, and so on…



So for example, if say the tags and rma addons both registered for the delete_product hook, the $hooks array would look like this.


$hooks
(
['delete_product'] => Array
(
[0] => Array
(
['func'] => 'fn_tags_delete_product'
['addon'] => 'tags'
['priority'] => 0
)

[1] => Array
(
['func'] => 'fn_rma_delete_product'
['addon'] => 'rma'
['priority'] => 0
)
)
)




Ya, kinda scary.



Now fn_set_hook() on the other hand is where we differ slightly I think. After digging in the guts of this function, my best explanation is that it simply looks at the $hooks array in the Registry object and invokes ALL of the ‘func’ values associated with a specific hook.



For example, the following code…


fn_set_hook(delete_product', $_REQUEST);



…will invoke fn_tags_delete_product and fn_rma_delete_product passing them $_REQUEST as a parameter.



So fn_register_hooks() is kinda like subscribing to an event while fn_set_hook() is the event itself. It seems to me that you would only use fn_set_hook() if you wanted to include specific hooks in your addon code so that other addons can hook into it. Most of the time I see addon developers using the pre-defined fn_set_hook()'s of the core code, which brings us roundabout to Fred’s original question, where can we find a list of the pre-defined core code hooks?



Answer = nowhere! The dev’s are too busy trying to read their own damn spagetti code to write doc, rofl!! I don’t think it’s really that important though. I’m thinking that once you know which controller process you want to modify, finding the appropriate hook will not be that hard.



My biggest concern is adding and retreiving addon specific data from the database during a hook operation. Anyone have any success with something like that yet?



Tim

Yep I agree with you Timothy, it is scary!



Snorocket were you been holmes? I know you know this stuff. Lead us closer to the path of righteous CS-Cart addon development. We are almost there!



P.S. How has this thread not existed before?? We few can’t be the only ones trying to make something new and exciting happen :wink:



P.S.II. I am relentless and will not let this ‘spagetti code’ beat me. Who wants to join me? haha.

I’m with you. What questions do we still need to answer? What do you want to look into next?

If you missed it, a mostly complete list of hooks is here



[url]http://forum.cs-cart.com/showthread.php?p=72015#post72015[/url]

Excellent. So now the two biggest original questions have been answered.


  1. How do hooks work? (both TH and CH)
  2. What hooks are available and where?



    The questions that are still unanswered for me are these.


  3. How do you include schema changes needed by your addon?
  4. How do you insert/update/query against these schema changes in your addon hook handler?



    I am pretty familiar with SQL and relational database techniques. I just need to know how to make it work within the constraints of the CS-Cart architecture.



    For example, my store will be selling tickets. Each ticket is valid for only a certain set of dates. I need to be able to associate each product (ticket) with the dates that it is valid for.



    If I were designing the tables myself, I would simply add a PRODUCT_AVAILABLE_DATES table with a structure something like the following…


PRODUCT_AVAILABLE_DATES
---------------------------------
ROW_ID (Primary Key)
PRODUCT_ID (Foreign Key to the Product ID)
DATE_AVAILABLE (the date that the ticket is valid for)




Then whenever I needed to view product data, I would simply JOIN this table to the products table (I don’t know the actual name yet) on the PRODUCT_ID key.



So I guess what I’m asking is, how can I do this within the addon framework?

You put the ALTER TABLE and CREATE TABLE statements in your addon.xml.

[QUOTE]You put the ALTER TABLE and CREATE TABLE statements in your addon.xml.[/QUOTE]



Okay, I assume you are referring to the tag as shown in the Addon XML doc.


/* Section of additional queries to the database */
ALTER TABLE ?:products ADD COLUMN test /* If the parameter for="install" or it is absent, a query is performed when installing the addon */
ALTER TABLE ?:products DROP COLUMN test /* If the parameter for="uninstall", a query is performed when deleting the addon */




This seems simple enough to get the necessary schema changes into place on Addon installation.



Is there anything else involved in the process? It seems too easy.

hey, this is great, I’m glad someone posted the list of hooks. that was such an annoyance. It should be featured PROMINENTLY in the documentation for the addon api.

Since this is a key part of building addons, there’s no excuse for a list of hooks with all associated args not being in the documentation. Anyone from cscart want to explain why it’s not in the documentation or when it will be? You can’t build addons without this info.

Fred,



In my experience, when doc doesn’t get written it’s usually because the related source / process is too new or changing to fast. The base code has to stabilize before doc is worth writing, especially on open source projects. In a way the source code is the doc.



Earlier I called the source spagetti code. I feel bad about that. It’s actually pretty good. Better than I could do, especially in php, which is probably my least familiar language.



You and Unique seem to be pretty experienced coders. I think it would be awesome if we worked together to solve the problems we each have to face. I think we’d learn a lot faster and get things finished earlier.



So with that in mind, what are the details of your project, where are you at on it and what do you have to accomplish next. I don’t mind going through the source. I’m pretty good at that, especially with some experienced feedback.



Unique, same with you. What is your status. What do you need to do next.



I gotta learn this software inside and out. This seems like a good way to do it. We’ve made a lot of progress so far. If we get good, there’s no reason why we shouldn’t be able to set up a kick ass store, make cash, flesh out the old resume, and have beautiful babes crawling over us like puppies.



Right?



:slight_smile:

I’m just learning cs-cart. I’m doing a side project with a shopping cart, and I’m using it because it does seem like basically good code.

The thing is, it doesn’t matter if the project is changing or not. As far as I can tell, there’s NO WAY to make a plugin that does anything meaningful without using the function hooks, and there’s no documentation.

My first modification was just a test to see if I could figure out the architecture, a way to add a paragraph of text that is associated with a category. There’s probably already a way to do that in cs-cart, but I wanted to learn the architecture.

So, I needed to add a box to the admin area for categories. I needed to know what function was called when a category was being updated in admin so I could hook into it. There’s no documentation, so I just traced back through the code till I found the function, which is ‘update_category’. Here’s the relevant part of init.php


[QUOTE]fn_register_hooks(

‘update_category’

);

[/QUOTE]



Now, according to the documentation, I’ve created a hook that is called every time that update_category is called. My addon is called “category_news” so the naming convention has me name my function ‘fn_category_news_update_category’. That function will get called with all the parameters that are passed to ‘update_category’ in the core code. Without a list of both the hook functions AND their parameters, I had to use the parameter list I found in the core source code. It is too much to ask of developers that they have to read all your source code to figure out how to develop for your platform. That’s a huge pain.

For what it’s worth, here is the code that handles the data passed in from my form in the admin view. I’m leaving out the templates, they’re simple too. All I did was add another form field in the category update area via my plugin.


[QUOTE]function fn_category_news_update_category($category_data, $category_id = 0, $lang_code = CART_LANGUAGE)

{

$data = array (

‘news_title’ => $_POST[‘category_news_headline’],

‘news_body’ => $_POST[‘category_news’],



);



$insertData = array (

‘news_title’ => $_POST[‘category_news_headline’],

‘news_body’ => $_POST[‘category_news’],

‘category_id’ => $_POST[‘category_id’]

);



#does it exist?

$checkExistingNews = db_get_row(“SELECT * from ?:category_news where category_id = ?i”, $category_id);

if (!$checkExistingNews) {

db_query(‘INSERT INTO ?:category_news ?e’, $insertData);

}

else {

db_query(‘UPDATE ?:category_news SET ?u WHERE category_id = ?i’, $data, $category_id);

}



return true;

}[/QUOTE]



This handles the process of saving the data, and it’s where the crucial piece of documentation is missing.

The part that handles the displaying of the data in the admin area (I haven’t built the customer view, as this was just an experiment) is simple. I just put a file in the addons directory that is under category_news->controllers->admin and is called “categories.post.php”. That means "run this code after the native “categories” code is called.



That code, if it matters, looks like this:

[QUOTE]if ($mode == ‘update’) {

// Assign news to categories



Registry::set(‘navigation.tabs.category_news’, array (

‘title’ => ‘Category News’,

‘js’ => true

));



$category_id = $_GET[‘category_id’];



$checkExistingNews = db_get_row(“SELECT * from ?:category_news where category_id = ?i”, $category_id);

if ($checkExistingNews) {

$view->assign(‘category_news’, str_replace(“\'”, “'”, $checkExistingNews[‘news_body’]));

$view->assign(‘category_news_headline’, str_replace(“\'”, “'”, $checkExistingNews[‘news_title’]));

}



}[/QUOTE]

My project is to modify how products are added to the database by adding several fields to the admin products add page. Also, I need to code to be stripped down to only HTML for an internal system that logs and keeps track of everything I do on the site.



In the end I want to add extra tabs to the product tabs section… and then have these extra tabs only show up if I have added content to that tab.



For instance, right now the tabs are limited to



Description, Features, Tags, Files, Attachments, Reviews



I want to make it possible for some products to have:



Description, Specs, Accessories, Warranty, Related Products, Features, Tags, Files, Attachments, Reviews



And others to possibly have just:



Description, Specs, Warranty, Features, Reviews



Right now I have it coded directly into the products.php, fn.catalog, .tpl files, js files and all kinds of other files. I am in dire need of taking the time to make this into an addon so my code can be centralized. Also, I can’t update until I make it into an addon :oops:



I also have to export all of the code in different formats (one without any javascript for an internal system, one that can be pretty script intense for my website) – I do this all with PHP code. In the future I plan to move around these different versions using web services, but for now I am going to stick with what I am doing until i figure out how to make an addon flawlessly as this is how my entire site and internal system handle adding products.



I know it sounds absurd, but I haven’t spent all that much time on it considering I have borrowed ideas from the exim.php, fn.catalog.php, and products.php to add all this code together for the importing / exporting stuff.



I will share my finalized design as soon as I time to get this stuff done.

you probably know this, but here’s some code to add a tab in the admin area:



Registry::set(‘navigation.tabs.category_news’, array (

‘title’ => ‘Category News’,

‘js’ => true

));

yep, thanks though.



its actually easier for the folks that add the products to see everything on one screen, so my product tabs are going to be in a drop down, and when the actual tab type is selected then it generates a wysiwyg text box for html and then they can view it just like in the front end.



So if they selected “Warranty” from the drop down, then it would generate a wysiwyg box with the label “Warranty” on the back end. Then the drop down box would move down below the wysiwyg box and if they selected another tab type, then it would generate another wysiwyg, and so on. Until either all tab-types are filled with content, or until the user saves.



Then when you go to the front end, the customer will only see tab types that were filled in.



I hope that made sense, but navigating through all of CS-Cart’s tabs is already a pain for some users, so I’m trying to design some stuff so that my product adding person doesn’t get lost in the Admin backend. :stuck_out_tongue:

Brilliant work Fred. It’s going to take me a while before I can add anything more to that. You seem to have solved the main workflow questions I had.



I will mention that Unique was correct about using the tag in the Addon.xml file to add/remove schema information to the database. Using the following Addon.xml file I was able to confirm that the schema was correctly updated during addon install and uninstall.




product_availability
Product Availability
1
200
active


CREATE TABLE `?:product_availability_dates` (`product_availability_id` MEDIUMINT( 8 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `product_id` MEDIUMINT( 8 ) UNSIGNED NOT NULL, `availability_date` DATE NOT NULL ) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci
DROP TABLE `?:product_availability_dates`





At the same time, I’ve seen a lot of “schema” folders within the official addon folders that are packaged with CS-cart. I haven’t been able to figure out how these work yet so I don’t want to assume there isn’t more involved in the above process. As far as I can tell, the content of these “schema” folders appears to be mainly php with a little xml. Right now I don’t have a clue what it’s for though.