I'm using bootstrap 3 and i have to set multiple themes ont my website depends on each client we have.
Client 1 : green theme;
Client 2 : purple theme;
Client 3 : grey theme..
And goes on...
Context
The solution has to be dynamic, the website looks like the same, juste color change.
For now i use the theme on url (get theme... i search for a better solution)
I can't make a specific css by client type, duplicated code -> maintain impossible.
My solution for now
I make a file in php called bootstrap-override.css.php
Wich contain on top this code :
<?php
header("Content-Type: text/css");
$defaultWhite = $white = '#FFFFFF';
$defaultGray1 = $gray1 = '#E7E7E7';
$defaultGray2 = $gray2 = '#CDCDCD';
$defaultGray3 = $gray3 = '#F2F2F2';
$defaultGrayBlue1 = $grayBlue1 = '#99AEBD';
$defaultGrayBlue2 = $grayBlue2 = '#495D6C';
$defaultBlue1 = $blue1 = '#0094D7';
$defaultBlue2 = $blue2 = '#004F9F';
if (isset($_GET['theme'])) {
switch ($_GET['theme']) {
case 'CA':
$grayBlue1 = '#008168';
$blue1 = '#009AA5';
$blue2 = '#2A3B48';
break;
case 'CE':
$defaultGrayBlue1 = $grayBlue1 = '#ABABAB';
$defaultGrayBlue2 = '#727274';
$blue1 = '#D70119';
$blue2 = '#D70119';
break;
// More client...
}
}
?>
And my css using those variables, I call it using this code :
$this->headLink()->prependStylesheet($this->basePath() . '/websiteName/css/bootstrap-override.css.php'.((!empty($_GET['theme'])) ? "?theme={$_GET['theme']}" : ''))
(ZF2 application)
But the switch case part is ugly and the get part too.., i can't leave this in that way if i have a new client type i will make this code more complexe... i wanted a more efficient way to do this.
If someone can help to improve this.
Edit : From comments, Store all theme in Database, with a 1/n relationship between client / theme is possible, I had already thought month ago... but my problem for this solution is to design it. If i store this in Database, i have to create a model layer to respect Zend\MVC best practises, and how i design my CSS in this architecture ?
If I understand the issue correctly I would suggest adding CSS files to the HeadLink.
You could have a 'default' CSS file which implements the shared styles for all clients and then a separate, theme specific, CSS file which then specifies the just the styling for CA, CE etc.
To apply this to the HeadLink view helper you can use an event listener, listening 'on render'.
For example
use Zend\ModuleManager\Feature;
use Zend\EventManager\EventInterface;
use Zend\Mvc\MvcEvent;
class Module implements BootstrapListenerInterface
{
public function onBootstrap(EventInterface $event)
{
$eventManager = $event->getApplication()->getEventManager();
$eventManager->attach(MvcEvent::EVENT_RENDER, [$this, 'addClientCss'], 100);
}
public function addClientCss(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
$config = $serviceManager->get('config');
if (! isset($config['custom_css']) || ! is_array($config['custom_css'])) {
return;
}
$viewPluginManager = $serviceManager->get('ViewPluginManager');
$headLink = $viewPluginManager->get('headlink');
$basePath = $viewPluginManager->get('basepath')();
foreach($config['custom_css'] as $cssFilePath) {
$headLink->appendStylesheet($basePath . $cssFilePath);
}
}
}
Then the configuration in the required module.config.php.
return [
'client_css' => [
'/some/css/path/to/styles.css',
'/another/path/to/css/file.css'.
],
];
This would ideally be located in a module that is specific for the client or project you are working on (and also last in the module list in application.config.php) so that this config is merged last.
Related
Is there any way to remove tabs from an MvxTabsFragmentActivity-inherited class? I mean, currently there's only AddTab<T>() method for adding tabs. But, what if I want to remove tabs?
TIA,
Pap
No - MvxTabsFragmentActivity doesn't provide any RemoveTab functionality currently.
The source for this activity is https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Droid.Fragging/MvxTabsFragmentActivity.cs - you should be able to use this as a starting point for your own needs.
UPDATE:
After following #Stuart's advice and-as I mentioned in my comment below-I added the source code for the MvxTabsFragmentActivity class to my project and added the following method-to remove all tabs-which was all I wanted:
public void RemoveAllTabs()
{
// First, detach the curent tab using SupportFragmentManager object.
if (_currentTab != null)
{
var tag = _currentTab.CachedFragment.Tag;
_currentTab.CachedFragment = SupportFragmentManager.FindFragmentByTag( tag );
if (_currentTab.CachedFragment != null && !_currentTab.CachedFragment.IsDetached)
{
var ft = SupportFragmentManager.BeginTransaction();
ft.Detach( _currentTab.CachedFragment );
ft.Commit();
SupportFragmentManager.ExecutePendingTransactions();
}
}
// Second remove all tabs from TabHost object
if (_tabHost != null)
_tabHost.ClearAllTabs();
// And lastly, empty our _lookup table(actually a Dictionary).
_lookup.Clear();
_currentTab = null; // Clear the current tab
}
I guess if someone wanted to have a specific tab removed he could use the SupportFragmentManager object and have something like this:
public void RemoveTab( string tag )
{
var fragment = SupportFragmentManager.FindFragmentByTag( tag );
if (fragment != null && ! fragment.IsDetached)
{
var ft = SupportFragmentManager.BeginTransaction();
ft.Detach( fragment );
ft.Commit();
SupportFragmentManager.ExecutePendingTransactions();
//_tabHost.TabWidget.RemoveView( fragment.View ); // Neither this..
//_tabHost.RemoveView( fragment.View ); // .. or this removed the tab from the Tabhost.
}
}
However, although the above code was successful at removing the fragment/view inside the tab, the tab itself remained-showing a blank/empty tab. I couldn't find a TabHost.RemoveTab() or TabHost.TabWidget.RemoveTab() methods and the TabHost.RemoveView()/TabHost.TabWidget.RemoveView() did not work.
Notes: I renamed the MvxTabsFragmentActivity to something else and included all copyright notices at the top of the class in my project. Thanks again to #Stuart.
Is it possible to have a wiki page with two tables reflecting the data from two different 3rd party sites?
If so, how to get it done? Will page templates be of any help here?
Short answer is no, there's no easy, built-in way to pull external content into a MediaWiki site. Allowing a third party to inject arbitrary content would be massive security risk.
Long answer is that anything is possible with extensions, either existing ones or ones you write yourself. The MediaWiki site has an entire category of listings for "Remote content extensions" that do this kind of thing in one form or another, with External Data looking particularly useful. You will need admin rights to install any of these, and you'll need to trust both the extension code and the data you pull in.
I already wrote exactly what you describe. Might be helpful for you.
# Define a setup function
$wgHooks['ParserFirstCallInit'][] = 'efStackOverflow_Setup';
# Add a hook to initialise the magic word
$wgHooks['LanguageGetMagic'][] = 'efStackOverflow_Magic';
function efStackOverflow_Setup( &$parser ) {
# Set a function hook associating the "example" magic word with our function
$parser->setFunctionHook( 'stag', 'efStackOverflow_Render' );
return true;
}
function efStackOverflow_Magic( &$magicWords, $langCode ) {
# Add the magic word
# The first array element is whether to be case sensitive, in this case (0) it is not case sensitive, 1 would be sensitive
# All remaining elements are synonyms for our parser function
$magicWords['stag'] = array(1, 'stag');
# unless we return true, other parser functions extensions won't get loaded.
return true;
}
function efStackOverflow_Render( $parser, $param1 = '', $param2 = '' ) {
// there was filtering
$modif = 0;
$cache_file_path = "cache/".$param1."_".$param2;
if (file_exists($cache_file_path))
$modif = time() - #filemtime ($cache_file_path);
if (file_exists($cache_file_path) and $modif < 60*60*24) {
return file_get_contents($cache_file_path);
}
$page = file_get_contents("http://www.google.com/rss/".$param1);
$xml = new SimpleXMLElement($page);
foreach ($xml as $key => $value) {
// do some
}
if (!empty($output))
file_put_contents($cache_file_path, $output);
return $output;
}
Mediawiki version was 1.16.
What would be the best way to change the source of an image according to a theme selected? Ideally you will just have a CSS for each theme and set the image as a backround for example (that is what I currently do).
However what I need to do now is use an actual image that is functional and is part of the presentation, it is not just an image that is part of the design. This image will look slightly different depending on the theme.
I am saving the theme that each user has selected in the database. I want to be able to change the source of the image when a user requests a page according to the theme in the database. I am using dependency injection (StructureMap), MVC 4 and EF 5. I want to somehow assign value to ViewBag.MyImagePath in my _Layout page and then all pages to just have src="#ViewBag.MyImagePath".
You could write a theme aware image helper:
public static class HtmlExtensions
{
public static IHtmlString ThemeAwareImage(
this HtmlHelper htmlHelper,
string image,
string alt = ""
)
{
var context = htmlHelper.ViewContext.HttpContext;
var theme = context.Session["theme"] as string;
if (string.IsNullOrEmpty(theme))
{
// the theme was not found in the session
// => go and fetch it from your dabatase
string currentUser = context.User.Identity.Name;
theme = GetThemeFromSomeDataStore(currentUser);
// cache the theme in the session for subsequent calls
context.Session["theme"] = theme;
}
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var img = new TagBuilder("img");
img.Attributes["alt"] = alt;
img.Attributes["src"] = urlHelper.Content(
string.Format("~/images/{0}/{1}", theme, image)
);
return new HtmlString(img.ToString(TagRenderMode.SelfClosing));
}
}
which could be used in your views to render those images:
#Html.ThemeAwareImage("foo.jpg", "This is the foo image")
As a better alternative to using the Session to store the current user theme you could cache it in a cookie or even better make it part of your routes in which case your site will be much more SEO friendly.
I’m trying to integrate Facebook Credits as a paying method using the as3-sdk. I managed to get “earn_credits” and “buy_credits” working. However the third and most important option, “buy_item”, doesn’t show up the pay dialog. Somehow the connection to the callback.php seems the reason for the issue. Note: I have typed in the callback URL in my apps settings, so I didn’t forget that. I use the example php file from the Facebook Developer docs.
This is my as3 code.
public static function buyItem ():void
{
var theAction:String = "buy_item";
var order_info:Object = { "item_id":"1a" };
var jOrder:String = JSON.encode(order_info);
var data:Object = {
action:theAction,
order_info:jOrder,
dev_purchase_params: {"oscif":true}
};
Facebook.ui("pay", data, purchaseCallback);
}
I think the json encoding might be the problem, but i'm not sure.
I use the example php file from the Facebook Developer docs (excerpt):
<?php
$app_secret = '***********************';
// Validate request is from Facebook and parse contents for use.
$request = parse_signed_request($_POST['signed_request'], $app_secret);
// Get request type.
// Two types:
// 1. payments_get_items.
// 2. payments_status_update.
$request_type = $_POST['method'];
// Setup response.
$response = '';
if ($request_type == 'payments_get_items') {
// Get order info from Pay Dialog's order_info.
// Assumes order_info is a JSON encoded string.
$order_info = json_decode($request['credits']['order_info'], true);
// Get item id.
$item_id = $order_info['item_id'];
// Simulutates item lookup based on Pay Dialog's order_info.
if ($item_id == '1a') {
$item = array(
'title' => '100 some game cash',
'description' => 'Spend cash in some game.',
// Price must be denominated in credits.
'price' => 1,
'image_url' => '**********************/banner1.jpg',
);
// Construct response.
$response = array(
'content' => array(
0 => $item,
),
'method' => $request_type,
);
// Response must be JSON encoded.
$response = json_encode($response);
}
Any help, is really appreciated.
Okay so I cannot confirm that this works but according to this forum, it does:
var title:String = "TITLE FOO";
var desc:String = "FOO";
var price:String = "200";
var img_url:String = [some image url];
var product_url:String = [some product url];
// create order info object
var order_info:Object = {
"title":title,
"description":desc,
"price":price,
"image_url":img_url,
"product_url":product_url
};
// calling the API ...
var obj:Object = {
method: 'pay.prompt',
order_info: order_info,
purchase_type: 'item',
credits_purchase: false
};
Facebook.ui('pay', obj, callbackFunction);
I see that this example differs from yours slightly on the AS3 side so hopefully this nfo will help you resolve your problem. I realize that this isn't the best way to go about answering questions but I can see after a couple of days on here, no one has a answered you so I figured anything could help at this point. :)
Thank you #Ascension Systems!
This worked out well and is much better than creating a pop-up via html, and using navigateToURL etc...
One caveat though, which caused your solution not to work for me initially:
If you are relying on the callback.php sample provided by Facebook ( at the end of this page: http://developers.facebook.com/docs/credits/callback/ ), then you need to add this tag to your order_info object:
var item_id:String = "1a";
var order_info:Object = {
"title":title,
"description":desc,
"price":price,
"image_url":img_url,
"product_url":product_url,
"item_id":item_id
};
Without item_id defined, the if statement in Facebook's callback.php ( if ($item_id == '1a') ... ) will fail, and you'll get an unpleasant window: "App Not Responding
The app you are using is not responding. Please try again later."
Suppose, I have 3 stores.
I want to disable a module in Store 2. I only want it to be enabled in Store 1 and Store 3.
I see that I can do it by:-
Going to System -> Configuration -> Advanced
Selecting desired store from Current Configuration Scope dropdown list.
But this does not work fully.
And, I also don't want to check store in the module code itself or create system configuration field for the module to check/uncheck store to enable/disable.
What I am expecting is by adding some code in app/etc/modules/MyNamespace_MyModule.xml. Can we do it this way?
To disable a module on the store scope, I've found it's possible to do it like this:
Move app/code/core/Mage/Core/Model/Config.php to app/code/local/Mage/Core/Model/Config.php
Inside Config.php find the method "loadModulesConfiguration" Don't change anything, but add the following code to make the method look like this.
public function loadModulesConfiguration($fileName, $mergeToObject = null, $mergeModel=null)
{
$disableLocalModules = !$this->_canUseLocalModules();
if ($mergeToObject === null) {
$mergeToObject = clone $this->_prototype;
$mergeToObject->loadString('<config/>');
}
if ($mergeModel === null) {
$mergeModel = clone $this->_prototype;
}
$modules = $this->getNode('modules')->children();
foreach ($modules as $modName=>$module) {
if ($module->is('active')) {
// Begin additional code
if((bool)$module->restricted) {
$restricted = explode(',', (string)$module->restricted);
$runCode = (isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default');
if(in_array($runCode, $restricted)) {
continue;
}
}
// End additional code
if ($disableLocalModules && ('local' === (string)$module->codePool)) {
continue;
}
if (!is_array($fileName)) {
$fileName = array($fileName);
}
foreach ($fileName as $configFile) {
$configFile = $this->getModuleDir('etc', $modName).DS.$configFile;
if ($mergeModel->loadFile($configFile)) {
$mergeToObject->extend($mergeModel, true);
}
}
}
}
return $mergeToObject;
}
The new code will cause the method to also check for a new node in the module xml file, <restricted>. If the node exists, the value would be a comma separated list of store codes that you do NOT want the module to load on. If you have multiple stores, the $_SERVER variable "MAGE_RUN_CODE" should be set with the current store code. If it's not set, the script will fallback to assuming the store code is "default" which is what it is by default unless for some bizarre reason you decide to change that in the backend.
A modules xml file could then look like this:
<?xml version="1.0"?>
<config>
<modules>
<MyPackage_MyModule>
<active>false</active>
<restricted>mystore1,mystore4,mystore5</restricted>
<codePool>local</codePool>
</MyPackage_MyModule>
</modules>
</config>
With this, the module will not even load while on the stores with a store code of mystore1, mystore4, or mystore5. The <restricted> tag is entirely optional, if you omit it the module will load as it normally would.
This configuration just disables module output in layout for frontend, but module controllers, event observers, admin pages, etc still working.
Also don't forget to specify your module name in layout files definition, otherwise all the layout file content will be loaded for a particular store:
<config>
<layout>
<module_alias module="Module_Name">
<file>yourlayoutfile.xml</file>
</module_alias>
</layout>
</config>
If you are developing a module and want to disable full its functionality on the frontent for a particular store, then you should create a configuration field of "Yes/No" type and check its value via Mage::getStoreConfigFlag('config/field/path') in your module code.
I was using Eric solution for a while. In my case I disabled certain module responsible for Layered Navigation in one of my shops - thus returning to default Layered Navigation behaviour.
And it looked like its working, but after a while I've noticed that layered navigation options stopped to appear where they should. Soon I've noticed that in fact the module that should not work on this shop continued to work. Then I realized that when I disable configuration cache Eric's solution works, but after enabling it again it stops.
After a while I realized it had to work that way, with configuration cache enabled, because Eric's solution includes (or not) specified config files in global xml only while this xml is being generated. Then its cached and called from cache only. So when it was generated from site which should use some module it was included, and then used also on site which wasn't suppose to use it.
Anyway I worked out another solution, based on Eric's code (using restricted in modules config). I thought Magento should decide what to load when class is being requested. Then it could check what is current MAGE_RUN_CODE and use it dynamically.
There is a method in Mage_Core_Model_Config which is responsible for getting class name: getGroupedClassName.
Here is the code I used there:
if (strpos($className, 'Pneumatig_') !== false) {
$var = substr($className, 0, strpos($className, '_', strpos($className, '_') + 1));
if (isset($this->_xml->modules->$var)) {
if ((bool)$this->_xml->modules->$var->restricted === true) {
$code = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default';
if (strpos((string)$this->_xml->modules->$var->restricted, $code) !== false) {
$className = '';
}
}
}
}
This Pneumatig condition is because all my modules start from Company name, so i wanted to avoid not necessary processing, but its optional, code should work without it, or you can change it to anything else.
Then I get actual module name [Company]_[Module], and then check if its enabled in _xml (which is current configuration object). If it is restricted I clear $className so it force Magento to load the default in next line.
And this code is added just before is empty condition:
// Second - if entity is not rewritten then use class prefix to form class name
if (empty($className)) {
if (!empty($config)) {
$className = $config->getClassName();
}
if (empty($className)) {
$className = 'mage_'.$group.'_'.$groupType;
}
if (!empty($class)) {
$className .= '_'.$class;
}
$className = uc_words($className);
}
$this->_classNameCache[$groupRootNode][$group][$class] = $className;
return $className;
And for your convenience i paste whole getGroupedClassName code:
public function getGroupedClassName($groupType, $classId, $groupRootNode=null)
{
if (empty($groupRootNode)) {
$groupRootNode = 'global/'.$groupType.'s';
}
$classArr = explode('/', trim($classId));
$group = $classArr[0];
$class = !empty($classArr[1]) ? $classArr[1] : null;
if (isset($this->_classNameCache[$groupRootNode][$group][$class])) {
return $this->_classNameCache[$groupRootNode][$group][$class];
}
$config = $this->_xml->global->{$groupType.'s'}->{$group};
// First - check maybe the entity class was rewritten
$className = null;
if (isset($config->rewrite->$class)) {
$className = (string)$config->rewrite->$class;
} else {
/**
* Backwards compatibility for pre-MMDB extensions.
* In MMDB release resource nodes <..._mysql4> were renamed to <..._resource>. So <deprecatedNode> is left
* to keep name of previously used nodes, that still may be used by non-updated extensions.
*/
if (isset($config->deprecatedNode)) {
$deprecatedNode = $config->deprecatedNode;
$configOld = $this->_xml->global->{$groupType.'s'}->$deprecatedNode;
if (isset($configOld->rewrite->$class)) {
$className = (string) $configOld->rewrite->$class;
}
}
}
//START CHECKING IF CLASS MODULE IS ENABLED
if (strpos($className, 'Pneumatig_') !== false) {
$var = substr($className, 0, strpos($className, '_', strpos($className, '_') + 1));
if (isset($this->_xml->modules->$var)) {
if ((bool)$this->_xml->modules->$var->restricted === true) {
$code = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default';
if (strpos((string)$this->_xml->modules->$var->restricted, $code) !== false) {
$className = '';
}
}
}
}
//END CHECKING IF CLASS MODULE IS ENABLED
// Second - if entity is not rewritten then use class prefix to form class name
if (empty($className)) {
if (!empty($config)) {
$className = $config->getClassName();
}
if (empty($className)) {
$className = 'mage_'.$group.'_'.$groupType;
}
if (!empty($class)) {
$className .= '_'.$class;
}
$className = uc_words($className);
}
$this->_classNameCache[$groupRootNode][$group][$class] = $className;
return $className;
}
My clients install of Magento 1.8.1.0 has a problematic module that breaks another site's menu on a multi-store setup. The solution above posted by Eric Hainer didn't work for this install, so I altered it slightly:
Instead of using $_SERVER['MAGE_RUN_CODE'], I used $_SERVER['SERVER_NAME']. Worked like a charm. :)
So instead of:
$runCode = (isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : 'default');
use:
$runCode = (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'www.site1.com');
and instead of:
<restricted>mystore1,mystore4,mystore5</restricted>
use:
<restricted>www.site2.com,www.site3.com</restricted>
obviously changing "www.site1.com", "www.site2.com", and "www.site3.com" with your own locations.
Thanks for the idea Eric :)
Also interesting solution ,
http://inchoo.net/ecommerce/magento/how-to-activatedeactivate-magento-module-per-a-website-level/