//Change this key to whatever typoscript object you need
$myTSKey = 'plugin.tx_myext_pi1';
//Create new TSParser object
$TSparser = t3lib_div::makeInstance('t3lib_TSparser');
//Load conf with myTSkey's config data
list(,$conf) = $TSparser->getVal($myTSKey, $GLOBALS['TSFE']->tmpl->setup);
Sunday, 4 December 2011
Load any typoscript in an extension
When you write an extension you get passed it's typoscript config (plugin.tx_myext_pi1). However I'm writing a service class that doesn't have access to this passed data so how do you get this information? The TS Parser:
Labels:
config,
extension,
php,
TYPO3,
typoscript
Friday, 11 November 2011
Get TYPO3 domain for FE plugin
To get the domain for your current TYPO3 install use:
t3lib_div::getIndpEnv('TYPO3_SITE_URL')
Maybe this is documented somewhere obvious but I couldn't find it easily...
t3lib_div::getIndpEnv('TYPO3_SITE_URL')
Maybe this is documented somewhere obvious but I couldn't find it easily...
Wednesday, 19 October 2011
Fix broken links in tt_news text
This relates to tt_news version 3.01, the problem may be present in other versions however.
For some reason the required typoscript processing for the RTE text field is commented out in the static typoscript, you can see it by viewing the template in the template analyser.
This leaves any links and other RTE related functions left unrendered, though the links are the most obvious problem. You'll probably see <link youadderss=""></link> tags in the source of the page which means you defiantly have this problem.
To fix this simply add:
plugin.tt_news.general_stdWrap parseFunc < tt_content.text.20.parseFuncto a top level template. You may need to clear the cache to see the changes.
Labels:
tt_news,
TYPO3,
typoscript
Friday, 7 October 2011
Magento External Login and Buy
I needed a way for a user to login to a Magento shop from an external website and have a selected product automatically added to their cart.
First off you'll need to create an API user, there are lots of tutorials out there on how to do this. Make sure this user has access to at least "Customers->Retrieve customer info" and "Category Inventory->Retrieve stock data".
You'll obviously need a form on your external site for the customer to enter their details and select which product they want to buy. The following is just an (untested) example form to give you a rough idea.
Please note: You'll notice I've included an iframe pointing to the shops login page, this is important as it will create the required cookies for the customer to login. If you don't include this iframe any customer who is missing the required cookies won't be logged in and will have nothing added to their cart!
Now when the user submits that data you'll have to check three things, that the user exists, the password is correct and that the product is in stock and available.
First off you'll need to create an API user, there are lots of tutorials out there on how to do this. Make sure this user has access to at least "Customers->Retrieve customer info" and "Category Inventory->Retrieve stock data".
You'll obviously need a form on your external site for the customer to enter their details and select which product they want to buy. The following is just an (untested) example form to give you a rough idea.
Please note: You'll notice I've included an iframe pointing to the shops login page, this is important as it will create the required cookies for the customer to login. If you don't include this iframe any customer who is missing the required cookies won't be logged in and will have nothing added to their cart!
<iframe src="https://shop.address/customer/account/login/" style="position:absolute;height:1px;width:1px;top:-100px;left:-100px;"></iframe> <form action="your_external_script.php" method="post"> <div class="login_field"> <label>Choose your product</label> <div class="product_option"> <label for="product_1">Product #1</label> <!-- Value below should be a real product in Magento that the user has access to! --> <input type="radio" name="login[product_id]" value="1" /> </div> <div class="product_option"> <label for="product_2">Product #2</label> <!-- Value below should be a real product in Magento that the user has access to! --> <input type="radio" name="login[product_id]" value="2" /> </div> <div class="product_option"> <label for="product_3">Product #3</label> <!-- Value below should be a real product in Magento that the user has access to! --> <input type="radio" name="login[product_id]" value="3" /> </div> </div> <div class="login_field"> <label for="login_username">Username</label> <input id="login_username" type="text" value="" name="login[username]" /> </div> <div class="login_field"> <label for="login_password">Password</label> <input id="login_password" type="password" value="" name="login[password]" /> </div> <div class="login_field"> <input type="submit" value="Buy selected product" /> </div> </form>
Now when the user submits that data you'll have to check three things, that the user exists, the password is correct and that the product is in stock and available.
<?php
/**
* Validates a Magento password
*
* @param string $password: The user entered password
* @param string $hash: The password hash from Magento customer info
* @return boolean Returns true if the password is valid
*/
function validatePassword($password, $hash) {
$hashArr = explode(':', $hash);
switch(count($hashArr)) {
case 1:
return md5($password) === $hash;
case 2:
return md5($hashArr[1].$password) === $hashArr[0];
}
return false;
}
$apiUrl = 'https://shop.address/api/?wsdl';
$apiUser = 'my_api_user';
$apiKey = 'my_api_key';
if(isset($_POST['login'])) {
try {
$client = new SoapClient($apiUrl);
$session = $client->login($apiUser, $apiKey);
} catch (Exception $e) {
echo '<p class="error">Network error, please try again later.</p>';
exit;
}
//Check selected product is in stock...
try {
$product = $client->call($session, 'product_stock.list', array(intval($_POST['login']['product_id'])));
} catch (Exception $e) {
echo '<p class="error">'.$e->getMessage().'</p>';
exit;
}
if(empty($product)) {
echo '<p class="error">Product doesn\'t exist.</p>';
exit;
} else if($product[0]['is_in_stock'] == 0 || $product[0]['qty'] == 0) {
//You could possible add the title of
//the product here, I am lazy though and didn't look up where to get it from
echo '<p class="error">The product you chose has sold out.</p>';
exit;
}
//Lookup customer record
list($customer) = $client->call(
$session,
'customer.list',
array(
array(
'email' => addslashes($_POST['login']['username'])
)
)
);
if(is_array($customer)) {
if(validatePassword($_POST['login']['password'], $customer['password_hash'])) {
echo '<form action="https://shop.address/login_buy.php" method="post">';
echo '<input type="hidden" name="login[username]" value="'.$customer['email'].'" />';
echo '<input type="hidden" name="login[password]" value="'.$customer['password'].'" />';
echo '<input type="hidden" name="login[product_id]" value="'.intval($_POST['login']['product_id']).'" />';
echo '<input type="submit" value="Proceed to checkout" />';
echo '</form>';
exit;
} else {
echo '<p class="error">Invalid login or password</p>';
exit;
}
} else {
echo '<p class="error">Invalid login or password</p>';
exit;
}
}
?>
This then posts data to a custom script you'll have to add to the index of your Magento shop:
<?php
// Load the Magento core
require_once 'app/Mage.php';
umask(0);
Mage::app()->setCurrentStore('default');
Mage::getSingleton("core/session", array("name" => "frontend"));
$session = Mage::getSingleton("customer/session");
//Log out any existing sessions
if(!$session->isLoggedIn()) {
$session->logout();
}
//Log user in
$login = Mage::getSingleton('core/app')->getRequest()->getPost('login');
$session->login($login['username'], $login['password']);
$productId = $login['product_id'];
header('Location: https://shop.address/checkout/onepage/add?product='.$productId.'&qty=1');
exit;
?>
Friday, 30 September 2011
SOAP-ERROR on localhost, Windows 7
I was having issues connecting to an external magento site's API via my local test server (apache, windows 7), kept getting the error:
SOAP-ERROR: Parsing WSDL: Couldn't load from 'https://shop.address/api/?wsdl' : failed to load external entity "https://shop.address/api/?wsdl"Turns out the problem was the https bit. Accessing the API via the non-secure http works fine. Obviously some bug there which I couldn't be bothered looking into at the moment.
Tuesday, 6 September 2011
Fix TYPO3's powermail custom css option
Powermail is a great little plugin, one thing I absolutely hate about it though is the automatic html it generates.
The biggest problem is it generates the form elements using unique id's which change every time you create a new form. This is fine till you want to style the form with css. Look at the problem you get:
Create a file called class.tx_powermailmods.php and place it in either a custom extension or just in typo3conf.
plugin.tx_powermail_pi1.template.fieldWrap = fileadmin/templates/tmpl_fieldwrap.html Now you can modify the template using the new marker, which I've used like so:
The biggest problem is it generates the form elements using unique id's which change every time you create a new form. This is fine till you want to style the form with css. Look at the problem you get:
<div class="tx_powermail_pi1_fieldwrap_html tx_powermail_pi1_fieldwrap_html_text tx_powermail_pi1_fieldwrap_html_151 even" id="powermaildiv_uid151">Essentially the only way to change the style of a form item is by using the unique id, so your style sheets break every time you want to copy the form. Now powermail has an option "Add CSS class" which I thought would solve the problem, however it only added the class to the child element (the input element in the above HTML), which to me was useless because I needed to style the parent div and label. The only way I could see of getting this working was using a powermail hook and overriding the code as follows:
<label for="uid151">First Name<span class="powermail_mandatory">*</span></label></div>
<input type="text" required="required" tabindex="1" id="uid151" class="powermail_name powermail_text powermail_uid151 invalid" value="" name="tx_powermail_pi1[uid151]">
Create a file called class.tx_powermailmods.php and place it in either a custom extension or just in typo3conf.
<?phpThis removes the custom class from the class attributes of the form inputs and creates a new marker CUSTOM_CLASS which you can use in the template, but first you'll have to enable this hook. In localconf.php or the custom extensions ext_localconf.php put the following:
class tx_powermail_mods {
function PM_FieldWrapMarkerHook($uid, $xml, $type, $title, $markerArray, $piVarsFromSession, $pObj) {}
$class = 'class="'; // start class tag}
$class .= ($pObj->pi_getFFvalue(t3lib_div::xml2array($pObj->xml), 'mandatory') == 1 ? 'validate-one-required ' : ''); // add required class if needed
$class .= 'powermail_' . $pObj->formtitle; // add form title
$class .= ' powermail_' . $pObj->type; // add input type
$class .= ' powermail_uid' . $pObj->uid; // add input uid
$class .= ' powermail_subuid' . $pObj->uid . '_' . $i; // add input subuid
$class .= '" '; // close tag
$pObj->markerArray['###CLASS###'] = $class;
$pObj->markerArray['###CUSTOM_CLASS###'] = ($pObj->class_f != '' ? ' ' . $pObj->class_f : ''); // add manual class
?>
<?phpNow you just have to add in the new custom marker, you'll need to modify the template tmpl_fieldwrap.html which you'll find in the powermail/templates directory (typo3conf/ext/powermail/templates). Create a copy of this file and place is somewhere in fileadmin (fileadmin/templates for example). Now modify the powermail config in a template:
//If your using a custom extension called powermail_mods:
require_once(t3lib_extMgm::extPath('powermail_mods') . 'class.tx_powermailmods.php');
//Or if the file is sitting in typo3conf
//require_once(PATH_typo3conf . 'class.tx_powermailmods.php');
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['powermail']['PM_FieldWrapMarkerArrayHook'][] = 'tx_powermail_mods'; ?>
plugin.tx_powermail_pi1.template.fieldWrap = fileadmin/templates/tmpl_fieldwrap.html Now you can modify the template using the new marker, which I've used like so:
<!-- ###POWERMAIL_FIELDWRAP_HTML_TEXT### begin -->This allows you to define a custom class and reuse it across multiple forms. Much better.
<div id="powermaildiv_uid###POWERMAIL_FIELD_UID###" class="tx_powermail_pi1_fieldwrap_html tx_powermail_pi1_fieldwrap_html_text tx_powermail_pi1_fieldwrap_html_###POWERMAIL_FIELD_UID### ###ALTERNATE### ###CUSTOM_CLASS###"###DIVJS###><!-- ###POWERMAIL_FIELDWRAP_HTML_TEXT### end -->
<label for="###LABEL_NAME###">###LABEL######DESCRIPTION######MANDATORY_SYMBOL###</label></div>
<input type="text" ###ONFOCUS######NAME######VALUE######CLASS######ID######SIZE######MAXLENGTH######READONLY######TABINDEX######ACCESSKEY######JS###/>
Thursday, 25 August 2011
Magento - Export Products with Full URLs
I needed to export a list of products with full URL links included. Normally you would just use the built in Import/Export functions, but these do not create the full URLs that I required.
This is what I came up with:
Save it as export.php in your root Magento folder and browse to it in your browser.
This is what I came up with:
<?php
// Load the Magento core
require_once 'app/Mage.php';
umask(0);
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$userModel = Mage::getModel('admin/user');
$userModel->setUserId(0);
// Load the product collection
$collection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*') //Select everything from the table
->addUrlRewrite(); //Generate nice URLs
/*
For this example I am generating a CSV file,
but you can change this to suit your needs.
*/
echo "title,sku,id,url\n";
foreach($collection as $product) {
//Load the product categories
$categories = $product->getCategoryIds();
//Select the last category in the list
$categoryId = end($categories);
//Load that category
$category = Mage::getModel('catalog/category')->load($categoryId);
echo '"'.$product->getTitle().'","'.
$product->getSku().'",'.
$product->getId().',"'.
//This will the proper URL, the base url is optional, though make sure you remove the trailing export.php (or whatever you name this file)
str_replace('export.php/','',Mage::getBaseUrl()).$product->getUrlPath($category).'"'.
"\n";
}
?>
Save it as export.php in your root Magento folder and browse to it in your browser.
Friday, 24 June 2011
jQuery plugin template
(function($) {
//Set default settings
//These will be overridden by the values passed to the plugin
var settings = {
foo: false,
bar: true
};
//Define public methods
var methods = {
init: function(options) {
return this.each(function() {
var $this = $(this),
data = $this.data('pluginName');
if(options) {
//Override plugin settings with options passed
$.extend(settings, options);
};
if(!data) {
//If data isn't set, plugin hasn't been run before
//so do your initial setup
//Set any persistent data
$this.data('pluginName', {
runCount: 0
});
$this.pluginName('foobar');
} else {
$this.pluginName('foobar');
}
});
},
foobar: function(options) {
return this.each(function() {
var $this = $(this),
data = $this.data('pluginName');
data.runCount++;
if(options) {
//Override plugin settings with options passed
$.extend(settings, options);
};
if(settings.foo) {
$this.append('<div>Foo ' + data.runCount + '</div>');
}
if(settings.bar) {
$this.append('<div>Bar ' + data.runCount + '</div>');
}
});
}
};
$.fn.pluginName = function(method) {
var $this = $(this);
if(methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if(typeof method === 'object' || ! method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.pluginName');
};
};
})(jQuery);You call this plugin like so:
jQuery('.mydiv').pluginName();
or custom settings:
jQuery('.mydiv').pluginName({foo:true,bar:false});
Once initialised you can use the public methods:
jQuery('.mydiv').pluginName('foobar', {bar:true});
Thursday, 23 June 2011
Simple front end editing with feeditadvanced
If you need to allow people to edit things other than pages and content on the front end of your TYPO3 site you can use some simple typoscript and feeditadvanced to achieve it. The following example lets you edit front end users on the storage page 13.
Then you just have to attach this to the page somewhere (eg page.10 < temp.recordEditor). You can edit any table this way (tt_address, tt_news, your own custom table) by changing the table and field selection.
Of course anyone who wants to edit the records will have to be logged into the back end and have proper access to the page/records. You can allow users from the front end to edit things by using the simulate back end user extension: http://typo3.org/extensions/repository/view/simulatebe/current/
You may need to setup feeditadvanced specially for this, I use the following TSConfig:
You seem to need reloadPageOnContentUpdate because I find the records don't get updated properly unless the page is reloaded.
temp.recordEditor = CONTENT
temp.recordEditor {
table = fe_users
select.pidInList = 13
renderObj = COA_INT
renderObj {
10 = TEXT
10 {
field = name
wrap =<div>|</div>
stdWrap.editPanel = 1
stdWrap.editPanel {
allow = edit,new,delete,hide
line = 10
label = %s
onlyCurrentPid = 0
previewBorder = 0
edit.displayRecord = 1
}
}
}
}Then you just have to attach this to the page somewhere (eg page.10 < temp.recordEditor). You can edit any table this way (tt_address, tt_news, your own custom table) by changing the table and field selection.
Of course anyone who wants to edit the records will have to be logged into the back end and have proper access to the page/records. You can allow users from the front end to edit things by using the simulate back end user extension: http://typo3.org/extensions/repository/view/simulatebe/current/
You may need to setup feeditadvanced specially for this, I use the following TSConfig:
FeEdit.disable = 0 FeEdit.useAjax = 0 FeEdit.reloadPageOnContentUpdate = 1 FeEdit.clickContentToEdit = 1 FeEdit.menuBar.disable = 1
You seem to need reloadPageOnContentUpdate because I find the records don't get updated properly unless the page is reloaded.
Labels:
feeditavanced,
TYPO3,
typoscript
Subscribe to:
Posts (Atom)