Nn web site building blocks

From Wiki at Neela Nurseries
Jump to: navigation, search

Wiki Main Page | User Page of Ted | Google search engine



Web Site Building Blocks
first article on Neela Nurseries' wiki, 2017 June



The following tools and web and programming frameworks are all part of Ted's volunteer efforts with ASI web site, and in-progress study of how to configure and customize shopping carts, article and document management pages, and easy-to-read easy-to-adjust web page layouts using CSS and third party, open source frameworks:


  • Linux package selection for constrained systems


Contents

^ MYSQL database server and client


^ MYSQL reset root password

MYSQL reset root password keywords, key phrases: mysqld_safe --skip-grant-tables

An important MYSQL database access recovery step is to reset the database server's root password in cases where that is lost. The following statement run in a mysqld_safe --skip-grant-tables session works, while some of the more standard and MYSQL 5.7 documented password change statements fail due to a bug in MYSQL

This string may work on newer versions of MYSQL, but fails on MYSQL 5.4.51:

   mysql> update mysql.user set authentication_string=password('MyNewPass') where user='root';

This password resetting line works for the now old MYSQL 5.4.51:

   mysql> update mysql.user set Password=password('new_password') where user='root';
  • PHP set up

^ Apache2 web server configuration

When setting up apache2 on Ubuntu 16.04.3 LTS host, following messages captures from this package and some PHP enabling modules:


Excerpt x -

  .
  .
  .

Selecting previously unselected package ssl-cert.
Preparing to unpack .../ssl-cert_1.0.37_all.deb ...
Unpacking ssl-cert (1.0.37) ...
Selecting previously unselected package apache2-dbg.
Preparing to unpack .../apache2-dbg_2.4.18-2ubuntu3.5_amd64.deb ...
Unpacking apache2-dbg (2.4.18-2ubuntu3.5) ...
Processing triggers for libc-bin (2.23-0ubuntu10) ...
Processing triggers for man-db (2.7.5-1) ...
Processing triggers for systemd (229-4ubuntu21.1) ...
Processing triggers for ureadahead (0.100.0-19) ...
Processing triggers for ufw (0.35-0ubuntu2) ...
Setting up libaprutil1-dbd-sqlite3:amd64 (1.5.4-1build1) ...
Setting up libaprutil1-ldap:amd64 (1.5.4-1build1) ...
Setting up liblua5.1-0:amd64 (5.1.5-8ubuntu1) ...
Setting up apache2-bin (2.4.18-2ubuntu3.5) ...
Setting up apache2-utils (2.4.18-2ubuntu3.5) ...
Setting up apache2-data (2.4.18-2ubuntu3.5) ...
Setting up apache2 (2.4.18-2ubuntu3.5) ...
Enabling module mpm_event.
Enabling module authz_core.
Enabling module authz_host.
Enabling module authn_core.
Enabling module auth_basic.
Enabling module access_compat.
Enabling module authn_file.
Enabling module authz_user.
Enabling module alias.
Enabling module dir.
Enabling module autoindex.
Enabling module env.
Enabling module mime.
Enabling module negotiation.
Enabling module setenvif.
Enabling module filter.
Enabling module deflate.
Enabling module status.
Enabling conf charset.
Enabling conf localized-error-pages.
Enabling conf other-vhosts-access-log.

Enabling conf security.
Enabling conf serve-cgi-bin.
Enabling site 000-default.
Setting up apache2-doc (2.4.18-2ubuntu3.5) ...
apache2_invoke: Enable configuration apache2-doc
Setting up libapache2-mod-php7.0 (7.0.25-0ubuntu0.16.04.1) ...

Creating config file /etc/php/7.0/apache2/php.ini with new version
php_invoke: Enable module xml for apache2 SAPI
php_invoke: Enable module dom for apache2 SAPI
php_invoke: Enable module tidy for apache2 SAPI
php_invoke: Enable module simplexml for apache2 SAPI
php_invoke: Enable module shmop for apache2 SAPI
php_invoke: Enable module posix for apache2 SAPI
php_invoke: Enable module iconv for apache2 SAPI
php_invoke: Enable module ftp for apache2 SAPI
php_invoke: Enable module phar for apache2 SAPI
php_invoke: Enable module ctype for apache2 SAPI
php_invoke: Enable module sockets for apache2 SAPI
php_invoke: Enable module xsl for apache2 SAPI
php_invoke: Enable module opcache for apache2 SAPI
php_invoke: Enable module gettext for apache2 SAPI
php_invoke: Enable module tokenizer for apache2 SAPI
php_invoke: Enable module fileinfo for apache2 SAPI
php_invoke: Enable module wddx for apache2 SAPI
php_invoke: Enable module pdo for apache2 SAPI
php_invoke: Enable module mysqli for apache2 SAPI
php_invoke: Enable module exif for apache2 SAPI
php_invoke: Enable module pdo_mysql for apache2 SAPI
php_invoke: Enable module sysvshm for apache2 SAPI
php_invoke: Enable module sysvmsg for apache2 SAPI
php_invoke: Enable module mysqlnd for apache2 SAPI
php_invoke: Enable module readline for apache2 SAPI
php_invoke: Enable module xmlwriter for apache2 SAPI
php_invoke: Enable module calendar for apache2 SAPI
php_invoke: Enable module xmlreader for apache2 SAPI
php_invoke: Enable module json for apache2 SAPI
php_invoke: Enable module sysvsem for apache2 SAPI
Module mpm_event disabled.
Enabling module mpm_prefork.
apache2_switch_mpm Switch to prefork
apache2_invoke: Enable module php7.0
Setting up libapache2-mod-php (1:7.0+35ubuntu6.1) ...
Setting up libapache2-mod-svn (1.9.3-2ubuntu1.1) ...
apache2_invoke: Enable module dav_svn
apache2_invoke: Enable module authz_svn
Setting up ssl-cert (1.0.37) ...
Setting up apache2-dbg (2.4.18-2ubuntu3.5) ...
Processing triggers for libc-bin (2.23-0ubuntu10) ...
Processing triggers for systemd (229-4ubuntu21.1) ...
Processing triggers for ureadahead (0.100.0-19) ...
Processing triggers for ufw (0.35-0ubuntu2) ...
Press Return to continue.


Apache2 and setting up virtual hosts, web sites:

  • https://www.digitalocean.com/community/tutorials/how-to-set-up-apache-virtual-hosts-on-ubuntu-14-04-lts
    SSL certificates and SSL redirection
  • SSL Self-signed certificate creation and config
  • Sub-domain creation under Ubuntu 16.04 LTS and similar Linux releases
  • Mediawiki work . . .
  • OpenCart 2.x install and config, 'Wish List' and 'To Do List' . . .


    ^ SGML And Related Mark-Up Languages


    ^ Cascading Style Sheets (CSS)



    ^ 2017-11-27 - Web Page Fonts


    ^ 2017-11-28 - Javascript tutorials, examples and frameworks


    These are the first topics on which Ted wants to gather together notes. Hard to remember all details regarding pitfalls encountered, solutions found, and ideas for improving the configuration and use experience of these softwares . . . - TMH

    ^ PHP Libraries

    Programmer James Heinrich has written a PHP library to thumbnail and manipulate image files in other ways. James' PHP library project is available at both GitHub and SourceForge,


    To obtain a copy of this project,

       $ git clone https://github.com/JamesHeinrich/phpThumb ./phpThumb
    

    Chosen directory for downloaded instance of phpThumb is outside of web doc root. Will see whether this works . . . running in the 'demo' directory of phpThumb find that PHP's usable memory is set to -1, a seeming default value which likely won't allow phpThumb to work properly. Have per instructions installed ImageMagick utilities as available through Ubuntu 16.04.x LTS package list. The configuration checking script phpThumb/demo/phpThumb.demo.check.php so far reports that no ImageMagick program found.

    Also noticed that config checker says phpThumb cache directory present, readable but not writable. Created a /var/cache/phpThumb directory and amended file phpThumb/phpThumb.config.php. Cache directory yet reported 'not writable'. Changed ownership on this directory from root:root to www-data:www-data. Ok now writable . . . and now ImageMagick also found! Hmm, interesting.

    Demo files are a little hard to parse at first glance, which hashes of hashes set up to hold multiple demos shown in a single web document. The following phpThumb demo file at SourceForge is also part of our download. This looks like best starting point to get started and running with basic thumb-nailing of images:

       http://phpthumb.sourceforge.net/index.php?source=phpThumb.demo.object.simple.php
    


    About phpThumb file caching, this project file has important info starting at about line 135:

       https://github.com/JamesHeinrich/phpThumb/blob/master/docs/phpthumb.readme.txt
    

    "Note: phpThumb.php is where the caching code is located, if you instantiate your own phpThumb() object that code is bypassed and it's up to you to handle the reading and writing of cached files."


    When using single object to process multiple images:

       http://phpthumb.sourceforge.net/index.php?source=phpThumb.demo.object.php
    


    Summary - phpThumb file edits to get simple demo working

    We place phpThumb project files in <web_document_root>/lib/phpThumb. Here we edit two files, those named phpthumb.config.php and phpthumb.class.php. In the config file we set phpThumb's high security password to something non-null and long, non-dictionary word string:

      45 $PHPTHUMB_CONFIG['high_security_password']      = '...';
    


    In the class file we amend line 70 to name a relative path to a cache directory for phpThumb:

       70         public $config_cache_directory                      = './cache'; // null;
    


    We still can't make work the phpThumb.demo.demo.php script. We seem unable to make a successful call to the primary script with HTTP 'get' type parameters. This is the script which manages thumbnail caching, the script named phpThumb.php . . .


    - 2018-03-06 TUE -

    Excerpt from phpThumb.config.php:

    222 // * Compatability settings
    223 $PHPTHUMB_CONFIG['disable_pathinfo_parsing']        = true;   // if true, $_SERVER[PATH_INFO] is not parsed. May be needed on some server configurations to allow normal behavior.
    224 $PHPTHUMB_CONFIG['disable_imagecopyresampled']      = false;  // if true, imagecopyresampled is replaced with ImageCopyResampleBicubic. May be needed for buggy versions of PHP-GD.
    225 $PHPTHUMB_CONFIG['disable_onlycreateable_passthru'] = true;   // if true, any image that can be parsed by getimagesize() can be passed through; if false, only images that can be converted to GD by ImageCreateFrom(JPEG|GIF|PNG) functions are allowed
    226 $PHPTHUMB_CONFIG['disable_realpath']                = false;  // PHP realpath() function requires that "the running script must have executable permissions on all directories in the hierarchy, otherwise realpath() will return FALSE". Set config_disable_realpath=false to enable alternate filename-parsing that does not use realpath() function (but also does not resolve symbolic links)
    227 
    228 
    


    - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -

    ^ Opencart Work

    2019-07-16 - Tuesday, some references, need to move these to 2019 notes on cart fixes and improvements:

      .
      .
      .
    

    In 2018 Aril Society web team using Opencart made some amendments to show quantities of store items available on the default, first search results pages. Also added a trivial test to check for zero quantity available and mark the item as "sold out" underneath the item image.

    In 2019 web team working to resolve an Opencart 2.3.0.2 issue where an order that holds the last item or items of a given store item can't be edited, somehow related to that item's quantity becoming zero at or about time of customer submitting given order. We're creating a separate wiki article for now to hold this work, as 2018 notes are incomplete and cover other topics,

      2019 Opencart amendments by ASI team
    


    2018 Opencart Amendments . . .

    How to show item quantity on the pages where items are shown during shopping:

    Within Opencart 2p3p0p2 there are at least two scripts named 'product.php'. One of these in ~/opencart-2p3p0p2/catalog/model/catalog/ defines the following public functions, and this file is mentioned in the second reference just above:

    $ grep -n function product.php 
    3:	public function updateViewed($product_id) {
    7:	public function getProduct($product_id) {
    59:	public function getProducts($data = array()) {           <-- this function starting on line 59 looks most promising
    209:	public function getProductSpecials($data = array()) {        for showing how to later access and send to browser the
    259:	public function getLatestProducts($limit) {                  quantity of each product found in a given search.  - TMH
    275:	public function getPopularProducts($limit) {
    291:	public function getBestSellerProducts($limit) {
    309:	public function getProductAttributes($product_id) {
    337:	public function getProductOptions($product_id) {
    376:	public function getProductDiscounts($product_id) {
    382:	public function getProductImages($product_id) {
    388:	public function getProductRelated($product_id) {
    400:	public function getProductLayoutId($product_id) {
    410:	public function getCategories($product_id) {
    416:	public function getTotalProducts($data = array()) {
    518:	public function getProfile($product_id, $recurring_id) {
    524:	public function getProfiles($product_id) {
    530:	public function getTotalProductSpecials() {
    
    $
    

    The token 'quantity' appears in product.php about eight or ten times, but only two of those times is it part of an assignment to a returned result, usually an array. These assignments appear respectively in functions getProduct($product_id) and getProductOptions($product_id).

    These two functions seem to query for and return a single product yet we want to see product quantities on the pages which show search results, often multiple products. The function named getProducts() seems more promising. Calls to this function are many throughout the sources of Opencart 2p3p0p2. A list of them developed via grep is posted at,

    Among these results a promising source file which may be closer to the one we need amend to show quantities on Opencart search result pages is named search.php. The other files which call this function are named after actions such as checkout which seem less related to the action of searching for items in the on-line store:

     ./catalog/controller/product/search.php:214:			$results = $this->model_catalog_product->getProducts($filter_data);
    

    There's also mention of two files in a different part of the Opencart installation:

    ./catalog/controller/common/cart.php:64:		foreach ($this->cart->getProducts() as $product) {
    ./system/library/cart/cart.php:33:	public function getProducts() {
    ./system/library/cart/cart.php:297:		foreach ($this->getProducts() as $value) {
    ./system/library/cart/cart.php:309:		foreach ($this->getProducts() as $product) {
    ./system/library/cart/cart.php:321:		foreach ($this->getProducts() as $product) {
    ./system/library/cart/cart.php:331:		foreach ($this->getProducts() as $product) {
    ./system/library/cart/cart.php:351:		foreach ($this->getProducts() as $product) {
    ./system/library/cart/cart.php:361:		$products = $this->getProducts();
    ./system/library/cart/cart.php:371:		return count($this->getProducts());
    ./system/library/cart/cart.php:379:		foreach ($this->getProducts() as $product) {
    ./system/library/cart/cart.php:389:		foreach ($this->getProducts() as $product) {
    ./system/library/cart/cart.php:399:		foreach ($this->getProducts() as $product) {
    


    2018-06-16 SAT Promising template file of Opencart which may allow for adding quantities to search results pages of the cart:

     /var/www/domainname/public_html/opencart-2p3p0p2/catalog/view/theme/default/template/product/category.tpl
    

    ^ searching for assignment to $product array

     817 ./catalog/controller/common/cart.php:65:                        if ($product['image']) {^M
     818 ./catalog/controller/common/cart.php:66:                                $image = $this->model_tool_image->resize($product['image'], $this->config->get($this->config->ge
     819 ./catalog/controller/common/cart.php:73:                        foreach ($product['option'] as $option) {^M
     820 ./catalog/controller/common/cart.php:95:                                $unit_price = $this->tax->calculate($product['price'], $product['tax_class_id'], $this->config->
     821 ./catalog/controller/common/cart.php:98:                                $total = $this->currency->format($unit_price * $product['quantity'], $this->session->data['curre
     822 ./catalog/controller/common/cart.php:105:                               'cart_id'   => $product['cart_id'],^M
     823 ./catalog/controller/common/cart.php:107:                               'name'      => $product['name'],^M
     824 ./catalog/controller/common/cart.php:108:                               'model'     => $product['model'],^M
     825 ./catalog/controller/common/cart.php:110:                               'recurring' => ($product['recurring'] ? $product['recurring']['name'] : ''),^M
     826 ./catalog/controller/common/cart.php:111:                               'quantity'  => $product['quantity'],^M
     827 ./catalog/controller/common/cart.php:114:                               'href'      => $this->url->link('product/product', 'product_id=' . $product['product_id'])^M
    

    In file along side cart.php, that is file ./catalog/controller/common/search.php what do the structure or class members 'load' and 'view' mean? Do these call the code which runs the database search and assigns results to the $products array?

    Figure x - source of file search.php:

    <?php
    class ControllerCommonSearch extends Controller {
            public function index() {
                    $this->load->language('common/search');
    
                    $data['text_search'] = $this->language->get('text_search');
    
                    if (isset($this->request->get['search'])) {
                            $data['search'] = $this->request->get['search'];
                    } else {
                            $data['search'] = '';
                    }
    
                    return $this->load->view('common/search', $data);
            }
    }
    

    We're having a hard time finding an explicit assignment to $products array. It may be that this variable is named differently in different parts of Opencart source code . . .


    ^ searching for Opencart 'load' function

    Searching for token 'load' starting in OC root, here are some clueful findings:

    
    
    ./system/startup.php:113:require_once(modification(DIR_SYSTEM . 'engine/loader.php'));
    
    ./system/framework.php:23:$loader = new Loader($registry);
    ./system/framework.php:24:$registry->set('load', $loader);
    

    It looks like finding out the meaning of Opencart 'load' function requires us to learn about an Opencart class named 'Loader' . . .

    Some further notes on files in [oc_root_dir]/system/engine:

      • registry.php . . . defines set(), get() and has() methods, where a registry instance is like a scoreboard,
      • event.php . . . defines __construct(), register(), trigger(), unregister(), removeAction()
      • action.php . . . defines __construct(), getId(), execute()

    In file system/engine/action.php there's some magic going on in function 'execute()', which loads files and creates new instances of classes dynamically . . .


    ^ searching for OC variable $products

    Searching for token 'products' . . .

    ./library/cart/cart.php:361:            $products = $this->getProducts();
    


    zzz

    ^ searching for OC function getProducts()

    There seem to be three instances of a function named getProducts() defined in three different files:

       ./search-04--token-getProducts-in-ocr.txt:1:./admin/model/catalog/product.php:343:      public function getProducts($data = array()) {
    
       ./search-04--token-getProducts-in-ocr.txt:13:./catalog/model/catalog/product.php:59:    public function getProducts($data = array()) {
    
       ./search-04--token-getProducts-in-ocr.txt:76:./system/library/cart/cart.php:33: public function getProducts() {
    

    In file ./admin/model/catalog/product.php function getProducts() appears to capture product quantities in a database query. How these are returned in an array named $rows is not yet clear . . . - TMH

    In file ./catalog/model/catalog/product.php function getProducts() makes a complicated database query, and further along its definition appears to capture product quantity like the previous definition. Here's a code snippet which hints at this, this code starting on line 158:

                    $sort_data = array(
                            'pd.name',
                            'p.model',
                            'p.quantity',
                            'p.price',
                            'rating',
                            'p.sort_order',
                            'p.date_added'
                    );
    

    But further examination looks like this may be a sub-query, providing an intermediate result which amends an encompassing query to the database. Need to confirm or rule out this case. - TMH

    Speaking of possible database sub-queries, the last lines of function getProducts in file 2 of 3 above, holds these following lines, and here worth noting that function getProducts() calls function getProduct():

    202                 foreach ($query->rows as $result) {
    203                         $product_data[$result['product_id']] = $this->getProduct($result['product_id']);
    204                 }
    205 
    206                 return $product_data;
    207         }
    

    QUESTION: is function getProduct() ultimately where a given product's quantity is either captured or omitted?


    ^ [open_cart_root]/system/engine/loader.php renders or executes PHP dot tpl files

    QUESTION: where and how are dot tpl files referenced in Opencart sources?

    ./system/storage/logs/error--opencart-default-store.log:1226:2018-06-17 17:24:20 - PHP Notice:  Undefined index: quantity in web_doc_root/public_html/opencart-2p3p0p2/catalog/view/theme/default/template/product/category.tpl on line 117
    ./system/storage/logs/error--opencart-default-store.log:1227:2018-06-17 17:54:13 - PHP Notice:  Undefined index: quantity in web_doc_root/public_html/opencart-2p3p0p2/catalog/view/theme/default/template/product/category.tpl on line 117
    ./system/storage/logs/error--opencart-default-store.log:1228:2018-06-17 18:05:09 - PHP Notice:  Undefined index: quantity in web_doc_root/public_html/opencart-2p3p0p2/catalog/view/theme/default/template/product/category.tpl on line 120
    ./system/storage/upload/temp-ld0HvNVaFCYzYxm71hXcqNjio5t1JWbe/install.xml:194:  <file path="admin/view/template/catalog/product_list.tpl">
    ./system/storage/upload/temp-LlRRskjDwZ966LGbGtkmzfNu6RcYcIS7/install.xml:194:  <file path="admin/view/template/catalog/product_list.tpl">
    ./system/storage/upload/temp-bi7quqdndkQo3DrxVdKN2HJ1qnZiCdkH/install.xml:194:  <file path="admin/view/template/catalog/product_list.tpl">
    ./system/engine/loader.php:95:                  $output = $template->render($route . '.tpl');^M
    


    ^ What We Know So Far

    Some key things we now know about code execution of Opencart's PHP scripts and template files:

      • [oc_root]/catalog/view/theme/default/template/product/category.tpl lays out HTML for each product shown in general search results,
      • [oc_root]/system/engine/loader.php references template or .tpl files by calling its $template->render() method,
      • [oc_root]/system/engine/action.php loads Opencart PHP files, some repeatedly

    The first of these three files, category.tpl contains PHP code which gets passed an array that's locally named $products. This array contains the attributes of each product which is found to match a given user search of the store catalog. Variable $products at this point holds a subset of the product attributes stored in OC database table named oc_products. While variables are often renamed when passed as parameters, we notice there are a few places where database query results get assigned to a $products variable. But we have not yet found the chain of data passing from that query assignment to the point of the array being read and sent out as HTML and content to the web browser.

    Specifically we are looking for which code effectively passes database query results to OC template 'category.tpl', in the array which category.tpl calls $products.

    Searching Opencart 2.3.0.2 sources for definitions of function 'render':

          1 grep -nr render\( ./* | grep -v javascript | grep function | grep render\( > ./search-08--function-render.txt
          2 
          3 ./system/library/template.php:19:       public function render($template) {^M
          4 ./system/library/pagination.php:13:     public function render() {^M
          5 ./system/library/template/tiwg.php:10:  public function render($template) {^M
          6 ./system/library/template/php.php:10:   public function render($template) {^
    

    Thinking on this search, function render() is called after $data has been assigned or amended general search results. In this sense render() is downstream of the data assignment we're looking for, which is the assignment of a database query to $products. We need to find where are the places that system/engine/loader.php view() function are called, and which of these calls occur during general searches by users who visit the store?


    ^ calls to system/engine/loader.php view()

    In Opencart root directory we invoke the search:

      $ grep -nr view\( ./*
    

    This brings up a lot of results, but the most interesting are:

    ./catalog/controller/product/special.php:284:		$this->response->setOutput($this->load->view('product/special', $data));
    ./catalog/controller/product/category.php:377:			$this->response->setOutput($this->load->view('product/category', $data));
    ./catalog/controller/product/category.php:429:			$this->response->setOutput($this->load->view('error/not_found', $data));
    ./catalog/controller/product/compare.php:156:		$this->response->setOutput($this->load->view('product/compare', $data));
    ./catalog/controller/product/search.php:491:		$this->response->setOutput($this->load->view('product/search', $data));
    ./catalog/controller/product/manufacturer.php:61:		$this->response->setOutput($this->load->view('product/manufacturer_list', $data));
    ./catalog/controller/product/manufacturer.php:362:			$this->response->setOutput($this->load->view('product/manufacturer_info', $data));
    ./catalog/controller/product/manufacturer.php:410:			$this->response->setOutput($this->load->view('error/not_found', $data));
    ./catalog/controller/product/product.php:478:			$this->response->setOutput($this->load->view('product/product', $data));
    ./catalog/controller/product/product.php:554:			$this->response->setOutput($this->load->view('error/not_found', $data));
    ./catalog/controller/product/product.php:558:	public function review() {
    ./catalog/controller/product/product.php:596:		$this->response->setOutput($this->load->view('product/review', $data));
    ./catalog/controller/product/product.php:629:				$this->model_catalog_review->addReview($this->request->get['product_id'], $this->request->post);
    ./catalog/controller/information/contact.php:158:		$this->response->setOutput($this->load->view('information/contact', $data));
    
    ./catalog/controller/information/contact.php:218:		$this->response->setOutput($this->load->view('common/success', $data));
    ./catalog/controller/information/information.php:48:			$this->response->setOutput($this->load->view('information/information', $data));
    ./catalog/controller/information/information.php:74:			$this->response->setOutput($this->load->view('error/not_found', $data));
    ./catalog/controller/information/sitemap.php:103:		$this->response->setOutput($this->load->view('information/sitemap', $data));
    
    ./catalog/controller/common/column_left.php:72:		return $this->load->view('common/column_left', $data);
    ./catalog/controller/common/cart.php:143:		return $this->load->view('common/cart', $data);
    ./catalog/controller/common/maintenance.php:30:		$this->response->setOutput($this->load->view('common/maintenance', $data));
    ./catalog/controller/common/header.php:150:		return $this->load->view('common/header', $data);
    ./catalog/controller/common/currency.php:49:		return $this->load->view('common/currency', $data);
    ./catalog/controller/common/footer.php:75:		return $this->load->view('common/footer', $data);
    ./catalog/controller/common/home.php:19:		$this->response->setOutput($this->load->view('common/home', $data));
    ./catalog/controller/common/column_right.php:72:		return $this->load->view('common/column_right', $data);
    ./catalog/controller/common/search.php:14:		return $this->load->view('common/search', $data);
    ./catalog/controller/common/language.php:45:		return $this->load->view('common/language', $data);
    

    . . . and out of these the most promising by name are:

    ./catalog/controller/product/compare.php:156:		$this->response->setOutput($this->load->view('product/compare', $data));
    ./catalog/controller/product/search.php:491:		$this->response->setOutput($this->load->view('product/search', $data));
    
    ./catalog/controller/common/search.php:14:		return $this->load->view('common/search', $data);
    


    ^ catalog/controller/product/compare.php calls remove() function . . .

    What's going on in catalog/controller/product/compare.php around line 14 and later line 95?

    Figure x - lines 14 plus:

         14                 if (isset($this->request->get['remove'])) {^M
         15                         $key = array_search($this->request->get['remove'], $this->session->data['compare']);^M
         16 ^M
         17                         if ($key !== false) {^M
         18                                 unset($this->session->data['compare'][$key]);^M
         19 ^M
         20                                 $this->session->data['success'] = $this->language->get('text_remove');^M
         21                         }^M
         22 ^M
         23                         $this->response->redirect($this->url->link('product/compare'));^M
         24                 }^M
    

    Figure x - lines 95 plus:

         95                                 if ($product_info['quantity'] <= 0) {^M
         96                                         $availability = $product_info['stock_status'];^M
         97                                 } elseif ($this->config->get('config_stock_display')) {^M
         98                                         $availability = $product_info['quantity'];^M
         99                                 } else {^M
        100                                         $availability = $this->language->get('text_instock');^M
        101                                 }^M
    


    First of these area per diagnostic 'd5' does not appear to be executing . . . so what about the assignment to an array named 'products' in catalog/controller/product/search.php?

    197:		$data['products'] = array();
      .
      .
      .
    


    ^ Top Down Manual Trace Of Opencart

    • [oc_root]/index.php
      calls start('catalog');
    • [oc_root]/system/startup.php
      function start() requires once SYSTEM_DIR/framework.php,

    Note: framework.php instantiates the following Opencart class objects:

    .../opencart-2p3p0p2/system$ grep -n new framework.php
     
    3:$registry = new Registry();
    6:$config = new Config();
    12:$event = new Event($registry);
    18:		$event->register($key, new Action($value));
    23:$loader = new Loader($registry);
    27:$registry->set('request', new Request());
    30:$response = new Response();
    36:	$registry->set('db', new DB($config->get('db_type'), $config->get('db_hostname'), $config->get('db_username'), $config->get('db_password'), $config->get('db_database'), $config->get('db_port')));
    40:$session = new Session();
    49:$registry->set('cache', new Cache($config->get('cache_type'), $config->get('cache_expire')));
    53:	$registry->set('url', new Url($config->get('site_base'), $config->get('site_ssl')));
    57:$language = new Language($config->get('language_default'));
    62:$registry->set('document', new Document());
    93:$controller = new Front($registry);
    98:		$controller->addPreAction(new Action($value));
    103:$controller->dispatch(new Action($config->get('action_router')), new Action($config->get('action_error')));
    
    $
    



    ^ Opencart Setting Table oc_settings

    Strange behavior going on where the sandbox / test store shows "quantity on hand" changes via files "template file .tpl six dirs below opencart root dir" and "product/category.php".


    ^ Tracking Down Add-To-Cart Appearances

    The word 'Availability' appears on singly selected products, and on this page which is not a search result itself there's an 'add to cart' button. This button appears also on category- and pattern-based search results pages along side or under each product displayed. We need to track down all places where this button is presented so that we may add PHP-based logic to Opencart's default scripts to replace this button with "SOLD OUT" message when product quantities in stock are zero . . .


    ^ Token 'Availability' appears on selected product page

    While the token 'Availability' appears only two times in Opencart 2.3.0.2 scripts and text files, the following pattern match results show this token assigned to two different variables among the scripts:

    [web_document_root]/opencart-2p3p0p2$ grep -nr Availability ./*
    ./catalog/language/en-gb/product/compare.php:12:$_['text_availability'] = 'Availability';
    ./catalog/language/en-gb/product/product.php:9:$_['text_stock']                = 'Availability';
    
    $
    


    References And Notes

    Name:            2017 plant sale item
    Description:     Aril Society International 2017 plant sale item.
    Meta tag title:  ASI 2017 plant sale item
    Meta tag description:
      Aril Society International 2017 plant sale item, iris rhizome bare root or collection (basket) of five arilbred irises.
    Meta tag keywords:
      iris sale, plant sale, ASI plant sale 2017
    



    ASI sale fees for handling, shipping, phytosanitation and taxes, per D Perry 2018-07-29:

        US -     $10 shipping,   $7.50 handling              = $17.50
    
        Canada - $20 shipping,   $7.50 handling,   $25 phyto = $52.50
    
        EU -     $40 shipping,   $7.50 handling,   $25 phyto = $72.50
    

    In Opencart without additional extensions installed, a tax rate can be created to realize a given fee like phytosanitation. Tax rates can be grouped under tax classes, so that for example a state tax and a phytosanitation fee can be added to an order bound to a particular state. Shipping rates in the default Opencart installation are set in in Opencart's Extensions area . . .


    ^ edit point - OC taxes and module duplication

    - 2018-07-01 Sunday -

    Opencart 2.3.0.2 duplicate module example


    ^ OC Tables Related To Products

    Question: when adding a product to an OC installation, a product without discounts or rewards, which tables are updated and in what fields per table? . . .

    mysql> show tables like '%product%';
    +------------------------------------+
    | Tables_in_oc_test_cart (%product%) |
    +------------------------------------+
    | oc_coupon_product                  |
    | oc_order_product                   |
    | oc_product                         |
    | oc_product_attribute               |
    | oc_product_description             |
    | oc_product_discount                |
    | oc_product_filter                  |
    | oc_product_image                   |
    | oc_product_option                  |
    | oc_product_option_value            |
    | oc_product_recurring               |
    | oc_product_related                 |
    | oc_product_reward                  |
    | oc_product_special                 |
    | oc_product_to_category             |
    | oc_product_to_download             |
    | oc_product_to_layout               |  <-- 2018-07-08 Question:  what does 'product to layout' mean?
    | oc_product_to_store                |
    | z-products-to-nn-test-store        |
    +------------------------------------+
    19 rows in set (0.00 sec)
    


    Of these OC tables with pattern 'product' in their names the following are empty in an OC cart installation that is 15 months in use:

      | oc_coupon_product . . . empty
      | oc_order_product . . . updated when products are ordered, when orders are submitted
      | oc_product . . . primary product attributes table, has thirty one (31) fields
      | oc_product_attribute . . . empty
      | oc_product_description . . . count of records matches count of records in table oc_products
      | oc_product_discount . . . empty
      | oc_product_filter . . . empty
      | oc_product_image . . . non-empty has six (6) records
      | oc_product_option . . . empty
      | oc_product_option_value . . . empty
      | oc_product_recurring . . . empty
      | oc_product_related . . . empty
      | oc_product_reward . . . empty
      | oc_product_special . . . empty
      | oc_product_to_category . . . has greater number records than table oc_products, (180) records
      | oc_product_to_download . . . empty
      | oc_product_to_layout . . . has greater number records than table oc_products, (555) records
      | oc_product_to_store . . . has greater number records than table oc_products, (443) records


    ^ Opencart tables described

    Table oc_product:

    +-----------------+---------------+------+-----+------------+----------------+
    | Field           | Type          | Null | Key | Default    | Extra          |
    +-----------------+---------------+------+-----+------------+----------------+
    | product_id      | int(11)       | NO   | PRI | NULL       | auto_increment |
    | model           | varchar(64)   | NO   |     | NULL       |                |
    | sku             | varchar(64)   | NO   |     | NULL       |                |
    | upc             | varchar(12)   | NO   |     | NULL       |                |
    | ean             | varchar(14)   | NO   |     | NULL       |                |
    | jan             | varchar(13)   | NO   |     | NULL       |                |
    | isbn            | varchar(17)   | NO   |     | NULL       |                |
    | mpn             | varchar(64)   | NO   |     | NULL       |                |
    | location        | varchar(128)  | NO   |     | NULL       |                |
    | quantity        | int(4)        | NO   |     | 0          |                |
    | stock_status_id | int(11)       | NO   |     | NULL       |                |
    | image           | varchar(255)  | YES  |     | NULL       |                |
    | manufacturer_id | int(11)       | NO   |     | NULL       |                |
    | shipping        | tinyint(1)    | NO   |     | 1          |                |
    | price           | decimal(15,4) | NO   |     | 0.0000     |                |
    | points          | int(8)        | NO   |     | 0          |                |
    | tax_class_id    | int(11)       | NO   |     | NULL       |                |
    | date_available  | date          | NO   |     | 0000-00-00 |                |
    | weight          | decimal(15,8) | NO   |     | 0.00000000 |                |
    | weight_class_id | int(11)       | NO   |     | 0          |                |
    | length          | decimal(15,8) | NO   |     | 0.00000000 |                |
    | width           | decimal(15,8) | NO   |     | 0.00000000 |                |
    | height          | decimal(15,8) | NO   |     | 0.00000000 |                |
    | length_class_id | int(11)       | NO   |     | 0          |                |
    | subtract        | tinyint(1)    | NO   |     | 1          |                |
    | minimum         | int(11)       | NO   |     | 1          |                |
    | sort_order      | int(11)       | NO   |     | 0          |                |
    | status          | tinyint(1)    | NO   |     | 0          |                |
    | viewed          | int(5)        | NO   |     | 0          |                |
    | date_added      | datetime      | NO   |     | NULL       |                |
    | date_modified   | datetime      | NO   |     | NULL       |                |
    +-----------------+---------------+------+-----+------------+----------------+
    31 rows in set (0.00 sec)
    


    Table oc_category:

    test_cart> describe oc_category;
    +---------------+--------------+------+-----+---------+----------------+
    | Field         | Type         | Null | Key | Default | Extra          |
    +---------------+--------------+------+-----+---------+----------------+
    | category_id   | int(11)      | NO   | PRI | NULL    | auto_increment |
    | image         | varchar(255) | YES  |     | NULL    |                |
    | parent_id     | int(11)      | NO   | MUL | 0       |                |
    | top           | tinyint(1)   | NO   |     | NULL    |                |
    | column        | int(3)       | NO   |     | NULL    |                |
    | sort_order    | int(3)       | NO   |     | 0       |                |
    | status        | tinyint(1)   | NO   |     | NULL    |                |
    | date_added    | datetime     | NO   |     | NULL    |                |
    | date_modified | datetime     | NO   |     | NULL    |                |
    +---------------+--------------+------+-----+---------+----------------+
    9 rows in set (0.00 sec)
    


    Opencart table oc_product_to_category:

    
    


    ^ product attributes used by ASI

    Tabs on OC's product editing page:

    General, Data, Links, Attribute, Option, Recurring, Discount, Special, Image, Reward Points, Design
    

    Attributes typically entered for ASI plant and seed sale items:

    General:
      * Product Name
        Description
      * Meta Tag Title
    
    Data:
      * Model 
        Price   
        Tax Class
        Quantity
        Minimum Qty       . . . default to one (1)?
        Subtract Stock
        Out Of Stock Status
        Requires Shipping
        Status  
    
    Links:
        Categories
        Stores  
    
    Attribute:  - none filled by ASI -
    Option:  - none filled by ASI -
    Recurring:  - none filled by ASI -
    Discount:  - none filled by ASI -
    Special:  - none filled by ASI -
    
    Image:
        ...set via OC admin pages, a graphical file-browse-and-select interface
    
    Reward Points:  - none filled by ASI -
    Design:  - none filled by ASI -
    


    Finding the highest product ID value in the products primary table:

      mysql> select max(product_id) from oc_product;
    


    ^ comma separated values to SQL (separate article)

    Spreadsheet to SQL conversion


    ^ Search for Opencart account creation code

    $ find . -name '*account*'
    ./catalog/language/en-gb/account
    ./catalog/language/en-gb/account/account.php
    ./catalog/language/en-gb/affiliate/account.php
    ./catalog/language/en-gb/extension/payment/klarna_account.php
    ./catalog/language/en-gb/extension/module/account.php
    ./catalog/model/account
    ./catalog/model/extension/payment/klarna_account.php
    ./catalog/controller/account
    ./catalog/controller/account/account.php
    ./catalog/controller/affiliate/account.php
    ./catalog/controller/extension/payment/klarna_account.php
    ./catalog/controller/extension/module/account.php
    ./catalog/view/theme/default/template/account
    ./catalog/view/theme/default/template/account/account.tpl
    ./catalog/view/theme/default/template/affiliate/account.tpl
    ./catalog/view/theme/default/template/extension/payment/klarna_account.tpl
    ./catalog/view/theme/default/template/extension/module/account.tpl
    ./admin/language/en-gb/extension/payment/klarna_account.php
    ./admin/language/en-gb/extension/module/account.php
    ./admin/controller/extension/payment/klarna_account.php
    ./admin/controller/extension/module/account.php
    ./admin/view/template/extension/payment/klarna_account.tpl
    ./admin/view/template/extension/module/account.tpl
    
    

    Opencart's Javascript-based drop down menus show the word "Register" for OC's menu option to create a customer account. Searching for this word among Opencart 2.3.0.2 files:

    ./admin/language/en-gb/setting/setting.php:27:$_['text_mail_account'] = 'Register';
    
    ./catalog/language/en-gb/common/header.php:8:$_['text_register'] = 'Register';
    ./catalog/language/en-gb/checkout/checkout.php:19:$_['text_register'] = 'Register Account';
    ./catalog/language/en-gb/account/register.php:3:$_['heading_title'] = 'Register Account';
    ./catalog/language/en-gb/account/register.php:7:$_['text_register'] = 'Register';
    ./catalog/language/en-gb/account/login.php:9:$_['text_register'] = 'Register Account';
    ./catalog/language/en-gb/affiliate/register.php:7:$_['text_register'] = 'Affiliate Register';
    ./catalog/language/en-gb/extension/module/affiliate.php:6:$_['text_register'] = 'Register';
    ./catalog/language/en-gb/extension/module/account.php:6:$_['text_register'] = 'Register';
    ./catalog/controller/checkout/register.php:2:class ControllerCheckoutRegister extends Controller {
    ./catalog/controller/account/register.php:2:class ControllerAccountRegister extends Controller {
    ./catalog/controller/affiliate/register.php:2:class ControllerAffiliateRegister extends Controller {
    


    selecting products of a model and finding their tax class id

    2018-07-28

      > select product_id, model, tax_class_id from oc_product where model='zzz default model';
    


    ^ OC presented web page header

    - 2018-07-25 WED -
    
    This file from OC root directory worth looking into:
    
    *  ./catalog/controller/common/header.php:85:           $data['contact'] = $this->url->link('information/contact');
    
    
    This pattern also worth searching for, from the HTML page source:
    
           <div id="top-links" class="nav pull-right">
    
    ...found in this file:
    
       ./catalog/view/theme/default/template/common/header.tpl
    


    - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - -

    ^ Errata

    Following data are specific to ASI 2018 plant sale, and not more general programmatic works or amendments to Opencart . . .

    ^ 2018 plant quantity changes

    changes to the sale:
        1.   REMOVE I. Kirkwoodiae from the sale
        2.   Change quantity on SIZZLE to 6
        3.   Change quantity on WINE AND LILAC to 2
        4.   Change quantity on I. hoogiana - dark form to 2
        5.   Change quantity on I. korolkowii to 4
        6.   Change quantity on I. paradoxa - Choschab to 2
    


    Table oc_customer:

    +-------------------+-------------+------+-----+---------+----------------+
    | Field             | Type        | Null | Key | Default | Extra          |
    +-------------------+-------------+------+-----+---------+----------------+
    | customer_id       | int(11)     | NO   | PRI | NULL    | auto_increment |
    | customer_group_id | int(11)     | NO   |     | NULL    |                |
    | store_id          | int(11)     | NO   |     | 0       |                |
    | language_id       | int(11)     | NO   |     | NULL    |                |
    | firstname         | varchar(32) | NO   |     | NULL    |                |
    | lastname          | varchar(32) | NO   |     | NULL    |                |
    | email             | varchar(96) | NO   |     | NULL    |                |
    | telephone         | varchar(32) | NO   |     | NULL    |                |
    | fax               | varchar(32) | NO   |     | NULL    |                |
    | password          | varchar(40) | NO   |     | NULL    |                |
    | salt              | varchar(9)  | NO   |     | NULL    |                |
    | cart              | text        | YES  |     | NULL    |                |
    | wishlist          | text        | YES  |     | NULL    |                |
    | newsletter        | tinyint(1)  | NO   |     | 0       |                |
    | address_id        | int(11)     | NO   |     | 0       |                |
    | custom_field      | text        | NO   |     | NULL    |                |
    | ip                | varchar(40) | NO   |     | NULL    |                |
    | status            | tinyint(1)  | NO   |     | NULL    |                |
    | approved          | tinyint(1)  | NO   |     | NULL    |                |
    | safe              | tinyint(1)  | NO   |     | NULL    |                |
    | token             | text        | NO   |     | NULL    |                |
    | code              | varchar(40) | NO   |     | NULL    |                |
    | date_added        | datetime    | NO   |     | NULL    |                |
    +-------------------+-------------+------+-----+---------+----------------+
    23 rows in set (0.00 sec)
    
    media:IBAB_V2.JPG
    AB iris "Ibab" photo by Rick Tasco

    Missing photo for AB iris "Ibab", photo contributed by Rick Tasco 2018 July 23


    ^ References

    - 2018-07-12 - Opencart product-to-layout . . .

    - 2018-07-21 -

    Excerpt from OC forum:

      " by nzplayer » Mon May 14, 2012 2:13 pm justinv wrote:I used this with success: Code: Select all <script type="text/javascript"> function pingServer() { $.ajax({ url: location.href }); } $(document).ready(function() { setInterval('pingServer()', 20000); }); </script> Put it in admin/view/template/common/header.tpl, right before the closing head tag: Code: Select all </head> It calls the page you're on and discards the result every 20000 milliseconds. Change the 20000 to whatever you need - every 60000 should be enough for even the worst web host (once a minute). I have written a VQmod with your code above in it (Thanks I needed it as well). "


    2018-07-24

      Example matching pattern: $string =~ m/([0-9]{2}[- ]?)?[0-9]{3}[- ]?[0-9]{4}/

    2018-11-08


    2019-03-12



    - - - top of page - - -