Jump to content



frankl

Member Since 03 Jan 2003
OFFLINE Last Active Today, 07:48
-----

#1755075 The problem 7%

Posted by frankl on 25 March 2017 - 11:29

Our business closely matches the Pareto Principle, where 80 percent of your outcomes come from 20 percent of your inputs.

 

80% of our time is spent on 20% of the customers, whereas 20% of our customers provide 80% of the revenue.

 

20% of complaints take 80% of our complaint handling time. 

 

I could go on, but you get the picture.

 

1. We automate everything we can. For example sending customers their parcel tracking number and a link to the courier's tracking website cut down on "Where is my parcel?" phone calls by 90%.

 

2. We never tell customers they aren't wanted. There aren't many we don't want. They can however, be "discouraged". 

 

3. See 2.




#1755062 Adding a External Link to a Website

Posted by frankl on 24 March 2017 - 22:55

@sunshynecraftsbeads

 

How to add an iframe in osCommerce?

 

Replace the content in your catalog/includes/english/carousel.php file with the following:

 

<?php
/*
  $Id$


  osCommerce, Open Source E-Commerce Solutions
  http://www.oscommerce.com


  Copyright (c) 2002 osCommerce


  Released under the GNU General Public License
*/


define('NAVBAR_TITLE', 'Carousel');
define('HEADING_TITLE', 'Carousel');


define('TEXT_INFORMATION', '<iframe title="Swarovski Create Your Style - Color Carousel" frameborder="0" src="http://www.create-your-style.com/Content.Node/Color-Carousel-External.en.php"width="642" height="1027"></iframe>');
?>

Replace the content in your catalog/carousel.php with the following:

 

<?php
/*
  $Id$


  osCommerce, Open Source E-Commerce Solutions
  http://www.oscommerce.com


  Copyright (c) 2010 osCommerce


  Released under the GNU General Public License
*/


  require('includes/application_top.php');


  require('includes/languages/' . $language . '/carousel.php');


  $breadcrumb->add(NAVBAR_TITLE, tep_href_link('carousel.php'));


  require(DIR_WS_INCLUDES . 'template_top.php');
?>


<div class="page-header">
  <h1><?php echo HEADING_TITLE; ?></h1>
</div>
<div class="contentContainer">
  <div class="contentText"><?php echo TEXT_INFORMATION; ?> 
  </div>


  <div class="buttonSet">
    <div class="text-right"><?php echo tep_draw_button(IMAGE_BUTTON_CONTINUE, 'fa fa-angle-right', tep_href_link('index.php')); ?></div>
  </div>
</div>


<?php
  require(DIR_WS_INCLUDES . 'template_bottom.php');
  require(DIR_WS_INCLUDES . 'application_bottom.php');
?>

This will work perfectly with one caveat -- if your carousel.php is a secure page (i.e. the URL starts with https:://) the content won't be displayed because modern browsers don't allow mixed content and the page you are trying to frame is insecure.

 

You can check this by opening your console (Control + Shift + I in Chrome and Firefox) and checking the error. You will see something like 

 

Mixed Content: The page at 'https://www.example.com/carousel.php'was loaded over HTTPS, but requested an insecure resource 'http://www.create-your-style.com/Content.Node/Color-Carousel-External.en.php'. This request has been blocked; the content must be served over HTTPS.

 




#1755021 How to make a 2.4 App

Posted by frankl on 24 March 2017 - 02:20

Did you say you want a hook for that?

 

Attached File  page_tab.gif   22.46KB   0 downloads

We can do that, but first we need to make a couple of minor changes to the categories.php page because it's not properly set up to take new page tabs.

in catalog/admin/categories.php change this
 

<div class="row">
  <div id="productLanguageTabs">
    <ul class="nav nav-tabs">

                
to this
 

<div class="row">
  <div id="productLanguageTabs">
    <ul class="nav nav-tabs2">

and this
 

<?php
      for ($i=0, $n=sizeof($languages); $i<$n; $i++) {
        echo '<li ' . ($i === 0 ? 'class="active"' : '') . '><a data-target="#section_general_content_' . $languages[$i]['directory'] . '" data-toggle="tab">' . $OSCOM_Language->getImage($languages[$i]['code']) . '&nbsp;' . $languages[$i]['name'] . '</a></li>';
      }
?>

                </ul>

                <div class="tab-content">

                
to this
 

<?php
      for ($i=0, $n=sizeof($languages); $i<$n; $i++) {
        echo '<li ' . ($i === 0 ? 'class="active"' : '') . '><a data-target="#section_general_content_' . $languages[$i]['directory'] . '" data-toggle="tab">' . $OSCOM_Language->getImage($languages[$i]['code']) . '&nbsp;' . $languages[$i]['name'] . '</a></li>';
      }
?>

                </ul>

                <div class="tab-content2">

All we've done is add the number 2 next to nav-tabs and tab-content.
               
We need to do that otherwise the extra tab that gets added in gets added in twice and screws up the display.

Perhaps @Harald Ponce de Leon will change this in the future to allow admin/products tab hooks. Or maybe I've done it the wrong way?

We need to tell osCommerce that we are introducing hooks into the categories page, so go back to the trusty oscommerce.json file and put in the required information.

Change this
 

    "modules": {
        "AdminMenu": {
            "Test":    "Module\\Admin\\Menu\\Test"
        },
        "Content": {
            "product_info": {
                "PIRELATED":    "Module\\Content\\PIRELATED"
            }

        }
    },

to this        
 

    "modules": {
        "AdminMenu": {
            "Related":    "Module\\Admin\\Menu\\Related"
        },
        "Content": {
            "product_info": {
                "PIRELATED":    "Module\\Content\\PIRELATED"
            }
        },
        "Hooks": {
            "Admin/Products": {
                "Action":    "Module\\Hooks\\Admin\\Products\\Action",
                "Page":        "Module\\Hooks\\Admin\\Products\\PageTab"
            }
        }

    },

    
Note: I'm now altering the files from the Github repository, so the Vendor, App, and Module names have changed from the posts above.

Now to create the actual hook, make a new folder OSC/Apps/FrankL/Related/Module/Hooks/Admin/Products

In that folder, create the Action file, Action.php
 

<?php
/**
  * Related Products App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 Frank Ludriks; https://www.ink-cartridge.com.au
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\FrankL\Related\Module\Hooks\Admin\Products;

use OSC\OM\HTML;
use OSC\OM\OSCOM;
use OSC\OM\Registry;

use OSC\Apps\FrankL\Related\Related as RelatedApp;

class Action implements \OSC\OM\Modules\HooksInterface
{
    protected $app;
    protected $ms;

    public function __construct()
    {
        if (!Registry::exists('Related')) {
            Registry::set('Related', new RelatedApp());
        }

        $this->app = Registry::get('Related');

        $this->ms = Registry::get('MessageStack');

        $this->app->loadDefinitions('hooks/admin/products/action');
    }

    public function execute()
    {

    
    }


}


and also a file PageTab.php
 

<?php
/**
  * Related Products App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 Frank Ludriks; https://www.ink-cartridge.com.au
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\FrankL\Related\Module\Hooks\Admin\Products;

use OSC\OM\HTML;
use OSC\OM\OSCOM;
use OSC\OM\Registry;

use OSC\Apps\FrankL\Related\Related as RelatedApp;

class PageTab implements \OSC\OM\Modules\HooksInterface
{
    protected $app;

    public function __construct()
    {
        if (!Registry::exists('Related')) {
            Registry::set('Related', new RelatedApp());
        }

        $this->app = Registry::get('Related');
    }

    public function display()
    {

        if (!defined('OSCOM_APP_FRANKL_RELATED_STATUS')) {
            return false;
        }

        $this->app->loadDefinitions('hooks/admin/products/tab');

        $output = '';

        $status = [];
        
        $info = '';
        
        $data = '';

        
        //check for masters
            $Qcheck = $this->app->db->prepare('select * from :table_products_related_products where pop_products_id_master = :products_id and pop_products_id_slave != :products_id');
            $Qcheck->bindInt(':products_id', $_GET['pID']);
            $Qcheck->execute();
            if ($Qcheck->fetch() !== false) {
                $master_query = 'select pop_products_id_slave
                                  from :table_products_related_products
                                  where pop_products_id_master = :products_id';
                $Qmaster = $this->app->db->prepare($master_query);
                $Qmaster->bindInt(':products_id', $_GET['pID']);
                $Qmaster->execute();
                
                $info .=  '<div class="col-sm-5"><table class="table table-hover">' .
                            ' <thead>' .
                            '    <tr class="info">' .
                            '      <th>' . $this->app->getDef('heading_master') . '</th>' .
                            '    </tr>' .
                            '  </thead>';
                
                  while ($Qmaster->fetch()) {
                
                    $related_query = 'select pa.*, pd.products_id, pd.products_name
                                      from :table_products_related_products pa
                                      left join :table_products_description pd
                                      on pa.pop_products_id_slave = pd.products_id
                                      and pd.language_id = :languages_id
                                      left join :table_products p
                                      on p.products_id = pd.products_id
                                      where pd.products_id = :products_id';
                    $Qrelated = $this->app->db->prepare($related_query);
                    $Qrelated->bindInt(':products_id', $Qmaster->value('pop_products_id_slave'));
                    $Qrelated->bindInt(':languages_id', $this->app->lang->getId());
                    $Qrelated->execute();
                  
                     while ($Qrelated->fetch()) {
                        $info .= '  <tr>' .
                            '    <td>' .
                            '      ' . $Qrelated->value('products_name') .
                            '   </td>' .
                            '  </tr>';
                    }
                   
                  }
                
                $info .=  '</table></div>';
                
            } else {
                
                $info .=    '<div class="col-sm-5"><table class="table table-hover">' .
                            ' <thead>' .
                            '    <tr class="info">' .
                            '      <th>' . $this->app->getDef('heading_master') . '</th>' .
                            '    </tr>' .
                            '  </thead>' .
                            '   <tr>' .
                            '      <td>' .
                            '        <div class="alert alert-warning col-sm-5" role="alert">' . $this->app->getDef('no_products') . '</div>' .
                            '      </td>' .
                            '    </tr>' .
                            '</table></div>';
                    
            }
                
        //check for slaves
                
            $Qcheck = $this->app->db->prepare('select * from :table_products_related_products where pop_products_id_slave = :products_id');
            $Qcheck->bindInt(':products_id', $_GET['pID']);
            $Qcheck->execute();
              if ($Qcheck->fetch() !== false) {
                $slave_query = 'select pop_products_id_master
                                from :table_products_related_products
                                where pop_products_id_slave = :products_id';
                $Qslave = $this->app->db->prepare($slave_query);
                $Qslave->bindInt(':products_id', $_GET['pID']);
                $Qslave->execute();
                
                
                $info .=  '<div class="col-sm-5"><table class="table table-hover">' .
                            ' <thead>' .
                            '    <tr class="info">' .
                            '      <th>' . $this->app->getDef('heading_slave') . '</th>' .
                            '    </tr>' .
                            '  </thead>';
                
                
                  while ($Qslave->fetch()) {
                
                    $related_query = 'select pa.*, pd.products_id, pd.products_name
                                      from :table_products_related_products pa
                                      left join :table_products_description pd
                                      on pa.pop_products_id_master = pd.products_id
                                      and pd.language_id = :languages_id
                                      left join :table_products p
                                      on p.products_id = pd.products_id
                                      where pd.products_id = :products_id and pa.pop_products_id_slave = :pop_products_id_slave';
                    $Qrelated = $this->app->db->prepare($related_query);
                    $Qrelated->bindInt(':products_id', $Qslave->value('pop_products_id_master'));
                    $Qrelated->bindInt(':pop_products_id_slave', $_GET['pID']);
                    $Qrelated->bindInt(':languages_id', $this->app->lang->getId());
                    $Qrelated->execute();

                     while ($Qrelated->fetch()) {
                        $info .= '  <tr>' .
                                 '    <td>' .
                                 '      ' . $Qrelated->value('products_name') .
                                 '   </td>' .
                                 '  </tr>';
                     
                     }
                  
                  }
                    
                    $info .=  '</table></div>';
                   
                   } else {
                $info .=    '<div class="col-sm-5"><table class="table table-hover">' .
                            ' <thead>' .
                            '    <tr class="info">' .
                            '      <th>' . $this->app->getDef('heading_slave') . '</th>' .
                            '    </tr>' .
                            '  </thead>' .
                            '   <tr>' .
                            '      <td>' .
                            '        <div class="alert alert-warning col-sm-5" role="alert">' . $this->app->getDef('no_products') . '</div>' .
                            '      </td>' .
                            '    </tr>' .
                            '</table></div>';
                    
              }
                
                

                $related_button = HTML::button($this->app->getDef('button_manage'), 'fa fa-arrows-h', $this->app->link('Admin&products_id_view=' . $_GET['pID']), null, 'btn-info');
                
                $data .= '<div class="panel panel-info oscom-panel">' .
                        '  <div class="panel-body">' .
                        '    <div class="container-fluid">' .
                        '      <div class="row">' .
                        '        ' . $info .
                        '      </div>' .
                        '      <div class="text-center">' .
                        '        ' . $related_button .
                        '      </div>' .
                        '    </div>' .
                        '  </div>' .
                        '</div>';
                
                $tab_title = addslashes($this->app->getDef('tab_title'));

                $output = <<<EOD
<div id="section_relatedAppRelated_content" class="tab-pane oscom-m-top-15">
  {$data}
</div>

<script>
$('#section_relatedAppRelated_content').appendTo('#productTabs .tab-content');
$('#productTabs .nav-tabs').append('<li><a data-target="#section_relatedAppRelated_content" data-toggle="tab">{$tab_title}</a></li>');
</script>
EOD;

        return $output;
    }


}


As usual, we need to create language definition files, so you need to create a new folder OSC/Apps/FrankL/Related/languages/english/hooks/admin/products
                                                    
and in that folder create a file tab.txt with the following contents
 

button_manage = Manage Related Products
tab_title = Related Products
no_products = None
heading_master = Master of...
heading_slave = Slave of...

That's it. Now go to a product edit page and you'll have a new tab called Related Products.

You can also check to see which hooks are installed by selecting Legacy -> Modules -> Hooks




#1754991 How to make a 2.4 App

Posted by frankl on 23 March 2017 - 06:25

This Related Products app is now complete, with full admin where you can add, edit, reciprocate, inherit, and delete related products.

 

If a dumb cut and paste coder like me can make an app, just about anyone can.

 

Please use this app (along with this thread and the developer docs https://library.osco...evelopers&apps) as a guide to make your own apps. If you do, please post them so everyone can see what is possible.

 

Check it out on Github -> https://github.com/f...ommerce-2.4-App

 

It will take you less than a minute to download, upload to your osCommerce 2.4 site, and install. Seriously! :thumbsup:




#1754984 How to make a 2.4 App

Posted by frankl on 22 March 2017 - 22:01

I will put this up on Github when I've changed the Vendor/App name throughout the app.

 

For now, let's get the content module working.

 

First of all we need to let osC know about the parameters, so that when we choose Configure (to configure modules) from the App menu they are all there for us to change.

As mentioned previously, all parameters go in the folder OSC/Apps/Test/Test/Module/Admin/Config/APPTEST/Params, and the corresponding language files go in OSC/Apps/Test/Test/languages/english/modules/APPTEST/Params

Some important points.

  • Each parameter has it's own parameter file.
     
  • The filename for the parameter is lower case and matches exactly the class name in the file. Extension is .php
     
  • The language define filename also matches the class name exactly, but with a .txt extension.
     
  • Upon module installation, the class/filename is made uppercase, added to the app configuration key prefix defined in OSC/Apps/Test/Test/Module/Admin/Config/ConfigAbstract.php (in this case $cut_length = strlen('OSCOM_APP_TEST_' . $this->code . '_'); which translates for our module app into OSCOM_APP_TEST_APPTEST_) and added to the database.

So, for example, the status parameter is defined in OSC/Apps/Test/Test/Module/Admin/Config/APPTEST/Params/status.php with a class name of status, and gets inserted into the database as OSCOM_APP_TEST_APPTEST_STATUS

Another example, the content_width parameter is defined in OSC/Apps/Test/Test/Module/Admin/Config/APPTEST/Params/content_width.php with a class name of content_width, and gets inserted into the database as OSCOM_APP_TEST_APPTEST_CONTENT_WIDTH

Rather than post every single parameter for this module, I'll zip them up and make them available for download (see below).

However, if you make an app, here are some examples of parameter files:

Status for a module (which is 1 or -1 so that osC can see if it's enabled or not)

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 osCommerce; https://www.oscommerce.com
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test\Module\Admin\Config\APPTEST\Params;

use OSC\OM\HTML;

class status extends \OSC\Apps\Test\Test\Module\Admin\Config\ConfigParamAbstract
{
    public $default = '1';
    public $sort_order = 1;

    protected function init()
    {
        $this->title = $this->app->getDef('cfg_apptest_status_title');
        $this->description = $this->app->getDef('cfg_apptest_status_desc');
    }

    public function getInputField()
    {
        $value = $this->getInputValue();

        $input = '<div class="btn-group" data-toggle="buttons">' .
                 '  <label class="btn btn-info' . ($value == '1' ? ' active' : '') . '">' . HTML::radioField($this->key, '1', ($value == '1')) . $this->app->getDef('cfg_apptest_status_true') . '</label>' .
                 '  <label class="btn btn-info' . ($value == '-1' ? ' active' : '') . '">' . HTML::radioField($this->key, '-1', ($value == '-1')) . $this->app->getDef('cfg_apptest_status_disabled') . '</label>' .
                 '</div>';

        return $input;
    }
}

A simple True/False parameter:
 

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 osCommerce; https://www.oscommerce.com
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test\Module\Admin\Config\APPTEST\Params;

use OSC\OM\HTML;

class show_name extends \OSC\Apps\Test\Test\Module\Admin\Config\ConfigParamAbstract
{
    public $default = 'True';
    public $sort_order = 30;

    protected function init()
    {
        $this->title = $this->app->getDef('cfg_apptest_show_name_title');
        $this->description = $this->app->getDef('cfg_apptest_show_name_desc');
    }

    public function getInputField()
    {
        $value = $this->getInputValue();

        $input = '<div class="btn-group" data-toggle="buttons">' .
                 '  <label class="btn btn-info' . ($value == 'True' ? ' active' : '') . '">' . HTML::radioField($this->key, 'True', ($value == 'True')) . $this->app->getDef('cfg_apptest_show_name_true') . '</label>' .
                 '  <label class="btn btn-info' . ($value == 'False' ? ' active' : '') . '">' . HTML::radioField($this->key, 'False', ($value == 'False')) . $this->app->getDef('cfg_apptest_show_name_disabled') . '</label>' .
                 '</div>';

        return $input;
    }
}

A parameter with text input:
 

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 osCommerce; https://www.oscommerce.com
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test\Module\Admin\Config\APPTEST\Params;

class name_length extends \OSC\Apps\Test\Test\Module\Admin\Config\ConfigParamAbstract
{
    public $default = '36';
    public $sort_order = 40;
    

    protected function init()
    {
        $this->title = $this->app->getDef('cfg_apptest_name_length_title');
        $this->description = $this->app->getDef('cfg_apptest_name_length_desc');
    }
}

A parameter with a select (i.e. for content width)
 

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 osCommerce; https://www.oscommerce.com
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test\Module\Admin\Config\APPTEST\Params;

use OSC\OM\HTML;

class content_width extends \OSC\Apps\Test\Test\Module\Admin\Config\ConfigParamAbstract
{
    public $default = '12';
    public $sort_order = 4;

    protected function init()
    {
        $this->title = $this->app->getDef('cfg_apptest_content_width_title');
        $this->description = $this->app->getDef('cfg_apptest_content_width_desc');
    }

    public function getInputField()
    {
        
        for ($i=1; $i<13; $i++) {
        $width_array[] = array('id' => $i, 'text' => $i);
    }


        $input = HTML::selectField($this->key, $width_array, $this->getInputValue());

        return $input;
    }
}

Here is an example of a language definition file:
 

cfg_apptest_status_title = Status
cfg_apptest_status_desc = Set this to Enabled to use the Product Info Page module.

cfg_apptest_status_true = Enabled
cfg_apptest_status_disabled = Disabled

Once uploaded, osC will pick them up the language definitions automatically so no need to add them to the parameters file.

There is also a setting you can add which will "lock" the parameter. Say, for instance you want to make the sort order fixed, add the line
 

public $app_configured = false;

to the file, like this
 

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 osCommerce; https://www.oscommerce.com
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test\Module\Admin\Config\APPTEST\Params;

class sort_order extends \OSC\Apps\Test\Test\Module\Admin\Config\ConfigParamAbstract
{
    public $default = '100';
    public $sort_order = 200;
    public $app_configured = false;

    protected function init()
    {
        $this->title = $this->app->getDef('cfg_apptest_sort_order_title');
        $this->description = $this->app->getDef('cfg_apptest_sort_order_desc');
    }
}

If you want to configure any parameters through the traditional module admin (for example Admin -> Legacy -> Modules -> Content) then add the line
 

public $app_configured = false;

to the parameter as above, then add the paramter to the module file (for example OSC/Apps/Test/Test/Module/Content/APPTEST.php) like this
 

    function keys() {
        return array('OSCOM_APP_TEST_APPTEST_SORT_ORDER');
    }

Note: I haven't done that with any of the parameters in this module.

Here are the parameters, which go in OSC/Apps/Test/Test/Module/Admin/Config/APPTEST/Params

Attached File  Params.zip   9.14KB   0 downloads

Here are the language definitions for the parameters, which go in OSC/Apps/Test/Test/languages/english/modules/APPTEST/Params

Attached File  LangParams.zip   4.1KB   0 downloads

Finally, the upgraded content module. As you recall, it was filled with dummy static content but now we want to display generated content.

Open OSC/Apps/Test/Test/Module/Content/APPTEST.php and replace the contents with this
 

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @[member='copyright'] (c) 2016 osCommerce; https://www.oscommerce.com
  * @[member='licensed2kill'] MIT; https://www.oscommerce.com/license/mit.txt
  */

  namespace OSC\Apps\Test\Test\Module\Content;

  use OSC\OM\HTML;
  use OSC\OM\OSCOM;
  use OSC\OM\Registry;
  use OSC\Apps\Test\Test\Test as TestApp;

  class APPTEST implements \OSC\OM\Modules\ContentInterface {
    public $code, $group, $title, $description, $sort_order, $enabled, $app;

    function __construct() {
      if (!Registry::exists('Test')) {
        Registry::set('Test', new TestApp());
      }

      $this->app = Registry::get('Test');
      $this->app->loadDefinitions('modules/APPTEST/APPTEST');

      $this->code = 'APPTEST';
      $this->group = 'product_info';

      $this->title = $this->app->getDef('module_apptest_title');
      $this->description = '<div align="center">' . HTML::button($this->app->getDef('module_apptest_legacy_admin_app_button'), null, $this->app->link('Configure&module=APPTEST'), null, 'btn-primary') . '</div>';
       
      
          if ( OSCOM_APP_TEST_APPTEST_STATUS < '1' ) {

            $this->enabled = false;
          } else {
            $this->enabled = true;
          }
    }
    
    function execute() {
      global $oscTemplate, $languages_id, $currencies, $currency, $PHP_SELF, $product_info;
      
      $content_width = OSCOM_APP_TEST_APPTEST_CONTENT_WIDTH;
      $product_width = OSCOM_APP_TEST_APPTEST_DISPLAY_EACH;

      $optional_rel_prods_content = NULL;
      
      $orderBy = 'order by ';
      $orderBy .= (OSCOM_APP_TEST_APPTEST_RANDOMIZE == 'True')?'rand()':'pop_order_id, pop_id';
      $orderBy .= (OSCOM_APP_TEST_APPTEST_MAX_DISP > 0)?' limit ' . OSCOM_APP_TEST_APPTEST_MAX_DISP:'';

      $optional_rel_prods = "SELECT ";
      $specials_query = '';

      if (OSCOM_APP_TEST_APPTEST_SHOW_QUANTITY == 'True')
          $optional_rel_prods .= "products_quantity, ";

      if (OSCOM_APP_TEST_APPTEST_SHOW_MODEL == 'True')
          $optional_rel_prods .= "products_model, ";

      if (OSCOM_APP_TEST_APPTEST_SHOW_THUMBS == 'True')
          $optional_rel_prods .= "products_image, ";
                                       
      if (OSCOM_APP_TEST_APPTEST_SHOW_PRICE == 'True') {
          $optional_rel_prods .= "products_tax_class_id, products_price, IF(s.status, s.specials_new_products_price, NULL) as specials_products_price, ";
          $specials_query = " left join :table_specials s on s.products_id = pb.products_id ";
      }

      if (OSCOM_APP_TEST_APPTEST_SHOW_NAME == 'True')
          $optional_rel_prods .= "products_name, ";        

      if (OSCOM_APP_TEST_APPTEST_SHOW_DESCRIPTION == 'True')
        $optional_rel_prods .= "substring_index(pa.products_description, ' ', " . OSCOM_APP_TEST_APPTEST_DESCRIPTION_LENGTH . ") as products_description, ";
        
      $optional_rel_prods .= "pop_products_id_slave ";
      
      $optional_rel_prods .= "from
                                                     :table_products_related_products,
                                                     :table_products pb
                              left join :table_products_description pa on pa.products_id = pb.products_id
                                                     " . $specials_query . "
                                                     where pop_products_id_slave = pa.products_id
                                                     and pa.products_id = pb.products_id
                                                     and language_id = :languages_id
                                                     and pop_products_id_master = :products_id
                                                     and products_status='1' " . $orderBy;
      
            $Qrelated = $this->app->db->prepare($optional_rel_prods);
            $Qrelated->bindInt(':languages_id', $this->app->lang->getId()); //(int)$languages_id
            $Qrelated->bindInt(':products_id', $_GET['products_id']); //(int)$_GET['products_id']
            $Qrelated->execute();

                if ($Qrelated->rowCount() > 0) {
 
                $optional_rel_prods_content .= '<h3>' . $this->app->getDef('module_apptest_title') . '</h3>';
        
                    $optional_rel_prods_content .= '  <div id="products" class="row list-group" itemtype="http://schema.org/ItemList">';
                    while ($Qrelated->fetch()) {                
                    if (OSCOM_APP_TEST_APPTEST_SHOW_QUANTITY == 'True')
                        $products_qty_slave = '<span itemprop="inventoryLevel">' . $Qrelated->value('products_quantity') . '</span>';
                    
                    $optional_rel_prods_content .= '  <div class="item col-sm-' .  $product_width . ' grid-group-item" itemprop="itemListElement" itemscope="" itemtype="http://schema.org/Product">';
                    $optional_rel_prods_content .= '    <meta itemprop="url" content="' . OSCOM::link('product_info.php', 'products_id=' . $Qrelated->value('pop_products_id_slave')) . '" />';


                    
                    $optional_rel_prods_content .= '    <div class="productHolder equal-height text-center">';

                    // show thumb image if Enabled
                    if (OSCOM_APP_TEST_APPTEST_SHOW_THUMBS == 'True') {
                        $optional_rel_prods_content .= '<a href="' . OSCOM::link('product_info.php', 'products_id=' . $Qrelated->value('pop_products_id_slave')) . '">' . HTML::image('images/' . $Qrelated->value('products_image'), $Qrelated->value('products_name'), SMALL_IMAGE_WIDTH, SMALL_IMAGE_HEIGHT) . '</a>';
                    }
      
                    $optional_rel_prods_content .= '      <div class="caption">';

                    if (OSCOM_APP_TEST_APPTEST_SHOW_NAME == 'True') {
                        $optional_rel_prods_content .= '<h2 class="group inner list-group-item-heading"><a href="' . OSCOM::link('product_info.php', 'products_id=' . $Qrelated->value('pop_products_id_slave')) . '"><span itemprop="name">' . $this->osc_truncate_text_rel_prod($Qrelated->value('products_name'), OSCOM_APP_TEST_APPTEST_NAME_LENGTH, OSCOM_APP_TEST_APPTEST_MAX_WORD_LENGTH) . '</span></a></h2 >';
                    }
                    
                    if (OSCOM_APP_TEST_APPTEST_SHOW_MODEL == 'True') {
                        $optional_rel_prods_content .=  '<p class="text-center small" itemprop="model">' . $Qrelated->value('products_model') . '</p>';
                    }
                    
                    $optional_rel_prods_content .= '<hr>';
                
            if (OSCOM_APP_TEST_APPTEST_SHOW_DESCRIPTION == 'True') {
                        $optional_rel_prods_content .= '<p class="text-center"><span itemprop="description">' . $this->osc_truncate_text_rel_prod(strip_tags($Qrelated->value('products_description')), OSCOM_APP_TEST_APPTEST_DESCRIPTION_LENGTH, OSCOM_APP_TEST_APPTEST_MAX_WORD_LENGTH) . '</span><br>';
            }

            if (OSCOM_APP_TEST_APPTEST_SHOW_PRICE == 'True') {
                $optional_rel_prods_content .= '<p class="text-center" itemprop="offers" itemscope itemtype="http://schema.org/Offer"><meta itemprop="priceCurrency" content="' . HTML::outputProtected($currency) . '">';
                if (tep_not_null($Qrelated->value('specials_products_price'))) {
                    $optional_rel_prods_content .= '<del>' . $currencies->display_price($Qrelated->value('products_price'), tep_get_tax_rate($Qrelated->value('products_tax_class_id'))) . '</del>&nbsp;';
                    $optional_rel_prods_content .= '<span class="productSpecialPrice" itemprop="price" content="' . $currencies->display_raw($Qrelated->value('specials_products_price'), tep_get_tax_rate($Qrelated->value('products_tax_class_id'))) . '">' . $currencies->display_price($Qrelated->value('specials_products_price'), tep_get_tax_rate($Qrelated->value('products_tax_class_id'))) . '</span><br>';
                } else {
                $optional_rel_prods_content .= '<span itemprop="price" content="' . $currencies->display_raw($Qrelated->value('products_price'), tep_get_tax_rate($Qrelated->value('products_tax_class_id'))) . '">' . $currencies->display_price($Qrelated->value('products_price'), tep_get_tax_rate($Qrelated->value('products_tax_class_id'))) . '</span><br>';
                }
                $optional_rel_prods_content .= '</p>';
            }
                    
                    if (OSCOM_APP_TEST_APPTEST_SHOW_QUANTITY == 'True') {
                        $optional_rel_prods_content .= '<p class="text-center" itemprop="offers" itemscope itemtype="http://schema.org/Offer">' . $this->app->getDef('module_apptest_products_quantity') . ' ' . $products_qty_slave . '</p>';
                    }
      
                    $optional_rel_prods_content .= '      </div>'; // caption
                    $optional_rel_prods_content .= '    </div>'; // thumbnail
                    $optional_rel_prods_content .= '  </div>'; // col-sm-' .  $product_width . '"
                }
                
                $optional_rel_prods_content .= '  </div>'; // end list of products
            }

            ob_start();
        include(__DIR__ . '/templates/APPTEST.php');
        $template = ob_get_clean();

        $oscTemplate->addContent($template, $this->group);
    }
    
    function isEnabled() {
      return $this->enabled;
    }

    function check() {
      return defined('OSCOM_APP_TEST_APPTEST_STATUS');
    }

    function install() {
      $this->app->redirect('Configure&Install&module=APPTEST');
    }

    function remove() {
      $this->app->redirect('Configure&Uninstall&module=APPTEST');
    }

    function keys() {
    }
    
        protected function osc_limit_text_rel_prod ($text, $maxchar, $wordlength = 40) {
        $text = str_replace ("\n", ' ', $text);
        $text = str_replace ("\r", ' ', $text);
        $text = str_replace ('<br>', ' ', $text);
        $text = wordwrap ($text, $wordlength, ' ', true);
        $text = preg_replace ("/[ ]+/", ' ', $text);
        $text_length = strlen ($text);
        $text_array = explode (" ", $text);

        $newtext = '';
        for ($array_key = 0, $length = 0; $length <= $text_length; $array_key++) {
            $length = strlen ($newtext) + strlen ($text_array[$array_key]) + 1;
            if ($length > $maxchar) break;
            $newtext = $newtext . ' ' . $text_array[$array_key];
        }

        return $newtext;
    }
    
    protected function osc_truncate_text_rel_prod ($products_text, $maxchar, $wordlength = 40) {
        $products_text = ($products_text);
            if ($maxchar > 0) {
                $products_text_length = strlen ($products_text);
                if ($products_text_length > $maxchar) {
                    $products_text = $this->osc_limit_text_rel_prod ($products_text, $maxchar, $wordlength);
                    $products_text .= '&nbsp;...';
                }
            }
                
            return $products_text;
   }
   
   
  }
?>

We also need a minor change to the language definition file for the module, so open up OSC/Apps/Test/Test/languages/english/modules/APPTEST/APPTEST.php

and replace the content with this

module_apptest_title = Related Products    
module_apptest_short_title = Related Products

module_apptest_legacy_admin_app_button = Manage App

module_apptest_introduction = This module will show the related products on the product info page
module_apptest_products_quantity = Qty Available:

Now you can go to Apps -> Test App -> Configure and install the content module. If it was already installed, uninstall it using the orange Uninstall... button on the bottom right then reinstall it. If you haven't yet related some products, go to Apps -> Test App -> Admin and do that now. Then navigate to the master product's page and you should see the related product/s.

Next, we'll upgrade the admin page so that we can edit, delete, reciprocate, and inherit related products.

 

Edit: fixed dumb spelling mistakes :)




#1754899 How to make a 2.4 App

Posted by frankl on 20 March 2017 - 20:54

I'll be posting more of my tutorial soon, stay posted!




#1754718 How to make a 2.4 App

Posted by frankl on 16 March 2017 - 10:32

@burt

 

Thanks Gary. Hopefully some people find it useful, I find the concept of Apps really exciting. Guess that makes me an osCommerce Nerd lol.




#1754687 How to make a 2.4 App

Posted by frankl on 15 March 2017 - 22:17

As @Harald Ponce de Leon said, when you go to make an actual app you will need vendor and app names to be your own, don't use Test for either unless it's strictly for testing purposes. Once this app is complete it will be packaged up under a different Vendor and App name.

 

Apps rely on a single admin page to administer the app. In this case it is a file called Home.php, with several controllers that take the required actions. You can think of it like php pages that show different things depending on the URL such as the categories.php page which has different actions depending on the variables (i.e. https://www.example....ion=new_product will edit Unreal Tournament in a fresh installation)

However the admin page does not have all the coding on one page like categories.php, rather it has separate classes in an Actions folder which take care of installation, configuration, and administration.

The main admin page also has templates which it accesses depending upon what actions the page needs to undertake.

We tell osCommerce which admin page to use through the oscommerce.json file in the "routes" section.

Apps also need an admin menu, which we also notify osCommerce of using another section in oscommerce.json called "AdminMenu".

Change oscommerce.json from
 

{
    "title":     "Test App",
    "app":       "Test",
    "vendor":    "Test",
    "version":    "0.0.1",
    "req_core_version":    "2.4",
    "license":    "MIT",
    "authors": [
        {
            "name":        "Author",
            "company":    "Company",
            "email":    "email@Example.com",
            "website":    "http://www.example.com"
        }
    ],
    "modules": {
        "Content": {
            "product_info": {
                "APPTEST":    "Module\\Content\\APPTEST"
            }
        }
    }
}


to
 

{
    "title":     "Test App",
    "app":       "Test",
    "vendor":    "Test",
    "version":    "0.0.1",
    "req_core_version":    "2.4",
    "license":    "MIT",
    "authors": [
        {
            "name":        "Author",
            "company":    "Company",
            "email":    "email@Example.com",
            "website":    "http://www.example.com"
        }
    ],
    "modules": {
        "AdminMenu": {
            "Test":    "Module\\Admin\\Menu\\Test"
        },
        "Content": {
            "product_info": {
                "APPTEST":    "Module\\Content\\APPTEST"
            }
        }
},
    "routes": {
        "Admin":    "Sites\\Admin\\Pages\\Home",
        "Shop": {
        }
    }
}

We've added an AdminMenu entry and a Routes/Admin entry

Apps also load their own language definitions, found in OSC/Apps/Test/Test/languages/YOUR LANGUAGE HERE, so go ahead and create that folder now (I used english, so it's OSC/Apps/Test/Test/languages/english).

To make an admin menu, create a folder OSC/Apps/Test/Test/Module/Admin/Menu

In that folder, create a file called Test.php and add
 

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @copyright (c) 2016 osCommerce; https://www.oscommerce.com
  * @licensed2kill MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test\Module\Admin\Menu;

use OSC\OM\Registry;

use OSC\Apps\Test\Test\Test as TestApp;

class Test implements \OSC\OM\Modules\AdminMenuInterface
{
    public static function execute()
    {
        if (!Registry::exists('Test')) {
            Registry::set('Test', new TestApp());
        }

        $OSCOM_Test = Registry::get('Test');

        $OSCOM_Test->loadDefinitions('admin/modules/boxes/test');

        $test_menu = [
            [
                'code' => $OSCOM_Test->getVendor() . '\\' . $OSCOM_Test->getCode(),
                'title' => $OSCOM_Test->getDef('module_admin_menu_install'),
                'link' => $OSCOM_Test->link()
            ]
        ];

        return array('heading' => $OSCOM_Test->getDef('module_admin_menu_title'),
                     'apps' => $test_menu);
    }
}


You may notice that the menu file makes use of aliasing, in this line use OSC\Apps\Test\Test\Test as TestApp;

All that means is that we are telling osCommerce that the full path to the class is OSC\Apps\Test\Test\Test (.php extension assumed) but it can reference it with the shorter alias of Testapp.

You can also see a new function loadDefinitions, which will grab the language defines for the menu from the file admin/modules/boxes/test (assumes .txt so you don't need to add that) in whatever language your admin is set to (in this case, we only have english)

The accompanying language file will be created in OSC/Apps/Test/Test/languages/english/admin/modules/boxes

In that folder create a file test.txt and add:

module_admin_menu_title = Test App
module_admin_menu_install = Install


At the moment we just have Install, we'll add more menu definitions later.

The link shown in the line 'link' => $OSCOM_Test->link() is the one we defined earlier in the oscommerce.json file under routes, Sites\\Admin\\Pages\\Home

That file, Home.php, is created in a folder OSC/Apps/Test/Test/Sites/Admin/Pages/Home

Put in that file
 

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @copyright (c) 2016 osCommerce; https://www.oscommerce.com
  * @licensed2kill MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test\Sites\Admin\Pages\Home;

use OSC\OM\Apps;
use OSC\OM\OSCOM;
use OSC\OM\Registry;

use OSC\Apps\Test\Test\Test;

class Home extends \OSC\OM\PagesAbstract
{
    public $app;

    protected function init()
    {
       
         $OSCOM_Test = new Test();
        Registry::set('Test', $OSCOM_Test);

        $this->app = $OSCOM_Test;
        
        $this->app->loadDefinitions('admin/install');
        
    }
}


This file on it's own won't do much.

We do have to make some more definitions though, it's asking for language definitions from admin/install so in the OSC/Apps/Test/Test/languages/english/admin folder create a file named install.txt (remembering that in the line $this->app->loadDefinitions('admin/install'); a .txt extension is assumed)

Put in that file
 

onboarding_intro_title = Related Products
onboarding_intro_body = <p>Manage related products and display in your store to drive further sales</p><p style="text-align: center;">{{button_install}}</p>

button_install = Install Now

We'll use those definitions in a template file in a moment.

We now need templates for the Home.php page to use. These go in the folder OSC/Apps/Test/Test/Sites/Admin/Pages/Home/templates

template_top.php
 

<?php
use OSC\OM\HTML;
use OSC\OM\OSCOM;
use OSC\OM\Registry;

$OSCOM_Test = Registry::get('Test');
$OSCOM_Page = Registry::get('Site')->getPage();
?>

<div class="row" style="padding-bottom: 30px;">

  <div class="col-sm-6 text-right text-muted">
    <?= $OSCOM_Test->getTitle() . ' v' . $OSCOM_Test->getVersion(); ?>
  </div>
</div>

<?php
if ($OSCOM_MessageStack->exists('Test')) {
    echo $OSCOM_MessageStack->get('Test');
}
?>

template_bottom.php can be blank

main.php is used to display the content when we select Install from the admin menu. Add to that file

<?php
use OSC\OM\HTML;

require(__DIR__ . '/template_top.php');
?>

<div class="row">
  <div class="col-sm-6">
    <div class="panel panel-primary">
      <div class="panel-heading"><?= $OSCOM_Test->getDef('onboarding_intro_title'); ?></div>
      <div class="panel-body">
        <?=
            $OSCOM_Test->getDef('onboarding_intro_body', [
                'button_install' => HTML::button($OSCOM_Test->getDef('button_install'), null, $OSCOM_Test->link(''), null, 'btn-primary')
            ]);
        ?>
      </div>
    </div>
  </div>
</div>

<?php
require(__DIR__ . '/template_bottom.php');
?>

At the moment the button (and page) don't do anything, but we'll work on that next.

 

You should now have a new menu item in the drop down Apps menu in your 2.4 admin which says Test App, with a sub menu saying Install. Clicking that should take you through to the installation page.




#1754634 How to make a 2.4 App

Posted by frankl on 14 March 2017 - 23:58

I'm going to attempt to make an app, and I'm going to post my methods here so that others can follow along and hopefully join in. With a bit of luck @Harald Ponce de Leon and others better at PHP coding will see this and make any corrections or give some help where needed.

 

Of course, all this is speculative as we don't know what the final form of oSC 2.4 will be, but as the Paypal App is in there and presumably is more or less complete we can use that as a template.

 

I am going to make a Related Products App, for two reasons. It is fairly simple, but also involves configuration, admin pages, and at least one content module.

 

OK, here goes:

 

First, let's get make the most basic module possible, one that put some static content on the product_info page.

Create a new directory in catalog\includes\OSC\Apps named Test

Then create a directory structure off that, so that you have

 

 

 

includes
    |_ OSC
        |_ Apps
            |_ Test
                |_ Test
                    |_ Module
                        |_ Content
                            |_ templates

 

 

 

In OSC\Apps\Test\Test, create a file named oscommerce.json

This will tell osCommerce where to find the app, who wrote it, and where the modules and routes can be found.

In the oscommerce.json file we will be giving our app the name of Test, and the content module, which shall be called APPTEST, will belong to the product_info group.

Put the following in this file and save it

{
    "title":     "Test App",
    "app":       "Test",
    "vendor":    "Test",
    "version":    "0.0.1",
    "req_core_version":    "2.4",
    "license":    "MIT",
    "authors": [
        {
            "name":        "Author",
            "company":    "Company",
            "email":    "email@Example.com",
            "website":    "http://www.example.com"
        }
    ],
    "modules": {
        "Content": {
            "product_info": {
                "APPTEST":    "Module\\Content\\APPTEST"
            }
        }
    }
}

Create another file in the OSC\Apps\Test\Test directory called Test.php. This will initiate the Test class so it can be used by osCommerce. For now it won't do anything, but we'll add some functions to it later.

Put the following in this file

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @copyright (c) 2016 osCommerce; https://www.oscommerce.com
  * @licensed2kill MIT; https://www.oscommerce.com/license/mit.txt
  */

namespace OSC\Apps\Test\Test;

class Test extends \OSC\OM\AppAbstract
{

    protected function init()
    {
    }

  }
?>

Now we will create the APPTEST module in OSC/Apps/Test/Test/Module/Content. This app will add some static (for now) content to the product info page. Create a file in this directory called APPTEST.php. I'm not sure if capitalization of the filename is important but as that's what the Paypal app does I'm going to do it too.

Add to this file:

<?php
/**
  * Test App for osCommerce Online Merchant
  *
  * @copyright (c) 2016 osCommerce; https://www.oscommerce.com
  * @licensed2kill MIT; https://www.oscommerce.com/license/mit.txt
  */

  namespace OSC\Apps\FrankL\Test\Module\Content;

  class APPTEST implements \OSC\OM\Modules\ContentInterface {
    public $code, $group, $title, $description, $sort_order, $enabled, $app;

    function __construct() {

      $this->app = 'Test';
      $this->code = 'APPTEST';
      $this->group = 'product_info';

      $this->title = 'Test App';
      $this->description = 'Just a test.';
      $this->sort_order = '10';
      
      $this->enabled = true;
    }

    function execute() {
      global $oscTemplate;


      ob_start();
      include(__DIR__ . '/templates/APPTEST.php');
      $template = ob_get_clean();

      $oscTemplate->addContent($template, $this->group);
    }
    
    function isEnabled() {
      return $this->enabled;
    }

    function check() {
    }

    function install() {
    }

    function remove() {
    }

    function keys() {
    }
  }
?>

We also need a template to display the actual content, so create a file in OSC/Apps/Test/Test/Module/Content/templates also called APPTEST.php and put the following content in it. This is just placeholder content, we'll change that to dynamically generated content later.

  <br />
  <div itemscope itemtype="http://schema.org/ItemList">
    <meta itemprop="itemListOrder" content="http://schema.org/ItemListUnordered" />
    <meta itemprop="numberOfItems" content="6" />

    <h3 itemprop="name">Test Content for Test App</h3>

    <div class="row">
      <div class="col-sm-6 col-md-4">  
        <div class="thumbnail"><a href="product_info.php?products_id=23"><img src="images/gt_interactive/wheel_of_time.gif" alt="The Wheel Of Time" title="The Wheel Of Time" width="100" height="80" /></a>
             <div class="caption"><h5 class="text-center" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a itemprop="url" href="product_info.php?products_id=23"><span itemprop="name">The Wheel Of Time</span></a><meta itemprop="position" content="1" /></h5>    
          </div>  
        </div>
      </div>
      <div class="col-sm-6 col-md-4">  
        <div class="thumbnail"><a href="product_info.php?products_id=1"><img src="images/matrox/mg200mms.gif" alt="Matrox G200 MMS" title="Matrox G200 MMS" width="100" height="80" /></a>    
          <div class="caption"><h5 class="text-center" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a itemprop="url" href="product_info.php?products_id=1"><span itemprop="name">Matrox G200 MMS</span></a><meta itemprop="position" content="2" /></h5>    
          </div>  
        </div>
      </div>
      <div class="col-sm-6 col-md-4">  
        <div class="thumbnail"><a href="product_info.php?products_id=2"><img src="images/matrox/mg400-32mb.gif" alt="Matrox G400 32MB" title="Matrox G400 32MB" width="100" height="80" /></a>    
          <div class="caption"><h5 class="text-center" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a itemprop="url" href="product_info.php?products_id=2"><span itemprop="name">Matrox G400 32MB</span></a><meta itemprop="position" content="3" /></h5>    
          </div>  
        </div>
      </div>
      <div class="col-sm-6 col-md-4">  
        <div class="thumbnail"><a href="product_info.php?products_id=25"><img src="images/microsoft/intkeyboardps2.gif" alt="Microsoft Internet Keyboard PS/2" title="Microsoft Internet Keyboard PS/2" width="100" height="80" /></a>    
          <div class="caption"><h5 class="text-center" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a itemprop="url" href="product_info.php?products_id=25"><span itemprop="name">Microsoft Internet Keyboard PS/2</span></a><meta itemprop="position" content="4" /></h5>    
          </div>  
        </div>
      </div>
      <div class="col-sm-6 col-md-4">  
        <div class="thumbnail"><a href="product_info.php?products_id=26"><img src="images/microsoft/imexplorer.gif" alt="Microsoft IntelliMouse Explorer" title="Microsoft IntelliMouse Explorer" width="100" height="80" /></a>    
          <div class="caption"><h5 class="text-center" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a itemprop="url" href="product_info.php?products_id=26"><span itemprop="name">Microsoft IntelliMouse Explorer</span></a><meta itemprop="position" content="5" /></h5>    
          </div>  
        </div>
      </div>
      <div class="col-sm-6 col-md-4">  
        <div class="thumbnail"><a href="product_info.php?products_id=27"><img src="images/hewlett_packard/lj1100xi.gif" alt="Hewlett Packard LaserJet 1100Xi" title="Hewlett Packard LaserJet 1100Xi" width="100" height="80" /></a>    
          <div class="caption"><h5 class="text-center" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a itemprop="url" href="product_info.php?products_id=27"><span itemprop="name">Hewlett Packard LaserJet 1100Xi</span></a><meta itemprop="position" content="6" /></h5>    
          </div>  
        </div>
      </div>    
    </div>
  </div>

That's our basic content created, but it won't show on the product info page until it gets installed.

The app is not yet installed, but if you want to see if the module works so far go to phpMyAdmin and search for the MODULE_CONTENT_INSTALLED configuration_key in the configuration table. Add at the end of the list of modules installed

;product_info/Test\Test\APPTEST

then navigate to product_info.php where you should see the Test App's contents.

 

Next I'm going to attempt to create an admin menu and installation and app configuration pages.




#1754539 17 Years Young!

Posted by frankl on 13 March 2017 - 04:26

@Harald Ponce de Leon

 

Now everyone will realise where those tep_ references came from :)




#1754426 Remove left and right columns on registration and checkout pages

Posted by frankl on 09 March 2017 - 04:16

I like this. Instead of hiding the columns with css it might be better to remove them completely so new pertinent columns could be added.

 

Hiding with css is just for demo purposes.

 

That's another good idea - different boxes for normal store and checkout.




#1754418 Remove left and right columns on registration and checkout pages

Posted by frankl on 09 March 2017 - 00:08

I've bought a lot of stuff online since before Christmas last year, and I notice that many online stores hide extraneous information during registration and checkout.

 

Do we need the left and right columns during checkout on the registration, shopping cart and checkout pages? Or even some of the navbar stuff on those pages? Could it be a configuration setting so that store owners could turn those features on or off during checkout?

 

Thinking out loud, turning off those unnecessary (at that stage of the purchase) elements would also give more space for promotional features such as cross selling, up selling, and newsletter signups etc.

 

Fairly simple to test, just upload this header tag and turn it on

 

Thoughts?

 

Attached File  no_columns_at_checkout.zip   3.96KB   29 downloads




#1753942 Autofilling forms

Posted by frankl on 28 February 2017 - 20:23

@burt

 

After careful consideration of the available material, the answer to that would be yes.




#1753919 PHP7.0 and osCommerce v2.2 RC2

Posted by frankl on 28 February 2017 - 00:09

@syscon

 

You aren't compliant, but that's on your shoulders.




#1753833 [Addon} Theme Switcher

Posted by frankl on 27 February 2017 - 01:03

@mdtaylorlrim

 

I've been using osCommerce 2.3.4BS on my production site since January 24, 2015 (over 2 years) and had sales of several million dollars since then. It's installed on hundreds, if not thousands, of other production stores too. It's finished, tested and proven :)