Yii 2 use same controller method / action in multiple controllers - yii2

I have a method checking whether user is logged in in my UserController.I need the same method in all the other controllers.How can I do the same without copy pasting the code to all controllers.
The controller method looks like
public function is_logged_in() {
$session = Yii::$app->session;
$cookies = Yii::$app->request->cookies;
//print_r($session);
$session->open();
$session_cookie_name = Yii::$app->params['cookie_name_session_var'];
$logged_in = false;
//echo "-memn-".$cook_name.' is halle - ';
//print_r($_SESSION);
if(($cook_name = $session->get($session_cookie_name))) {
//echo " - <pre>";
//print_r($cookies);
//exit;
$write_cookies = Yii::$app->response->cookies;
//echo "</pre>";
//echo $cookies->getValue($cook_name).' placenta';
if($u_token = $cookies->getValue($cook_name)) {
echo "b";
if($u_token) {
echo "c";
$write_cookies->remove($cook_name);
unset($write_cookies[$cook_name]);
$session->destroy();
$session->open();
$cookie_name = sha1($u_token).time();
$session[$session_cookie_name] = $cookie_name;
$write_cookies->add(new \yii\web\Cookie([
'name' => $session[$session_cookie_name],
'value' => $u_token,
'expire' => time() + 6000000
])); // around one hour expiry time
$session->close();
$logged_in = true;
//echo $u_token;
}
}
}
if(!$logged_in) {
$session->destroy();
}
return $logged_in;
}

1) You can create own component and put this method here or place it in the model (depends on logic of that method). Component can be placed for example in components folder (by default it doesn't exist). Then just use this component in any controllers you want.
2) If this code needs to be executed before or after certain actions, you can use behaviors.
3) You can use inheritance and create your custom controller that extends from yii\web\Controller, declare this method here and extend all other controllers where are you going to use this logic from your custom one.

In addition to arogachev's answer your code actually should sit in a class that extends the User component class http://www.yiiframework.com/doc-2.0/yii-web-user.html , not even to mention that the user identity class already does everything your code does (only much, much better). It comes with isGuest function.

Related

ACF overrides HTML attributes added with load_value hook

I'm using ACF together with the ACF Hook acf/load_value to add a custom HTML wrapper to the ACF value. I use then the ACFs to build an Elementor template (I'm using Elementor PRO).
The Template works and the values of ACFs are rendered, but the attribute I've added in the wrapper disappear
I've tried to change the priority of my filters, but it wasn't the problem. I 've also tried to look into the ACF settings, but seems that I cannot change this behavior just changing some settings.
This is the filter I made
if (!function_exists('my_acf_span_property')) {
function my_acf_span_property($value, $property) {
$value = '<span property="' . $property . '">' . $value . '</span>';
return $value;
}
}
if (!function_exists('my_acf_industry_value')) {
function my_acf_industry_value($value)
{
return my_acf_span_property($value, 'industry');
}
}
add_filter('acf/format_value/name=industry', 'my_acf_industry_value');
I made one filter for each ACF I need to change, this is only one as example.
I've tried to debug the filter changing return $value; to return htmlentities($value); in the function my_acf_span_property and the attributes are rendered in the frontend.
The output was expected to be <span property="industry">ACF value</span>
But wat is rendered is <span>ACF value</span>
It could be an Elementor problem?
Any Idea?
I solved with an action to allow the attributes in the posts
if (!function_exists('allow_property_attribute')) {
function allow_property_attribute() {
global $allowedposttags, $allowedtags;
$newattribute = "property";
$allowedposttags["span"][$newattribute] = true;
$allowedtags["span"][$newattribute] = true;
$allowedposttags["div"][$newattribute] = true;
$allowedtags["div"][$newattribute] = true;
$allowedposttags["p"][$newattribute] = true;
$allowedtags["p"][$newattribute] = true;
}
}
add_action( 'init', 'allow_property_attribute' );
It looks like was WordPress my problem, and not Elementor or ACF.

How to custom the theme of my website depends on user

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.

JSON API to show Advanced Custom Fields - WordPress

I am developing a magazine WordPress site that will have a json feed for a Mobile App. I set the backend up using Advanced Custom Fields with a Repeater Field for Multiple Articles and Multiple Pages within each article. http://www.advancedcustomfields.com/add-ons/repeater-field/
I am using the JSON API but this does not include any of my custom fields. Is there currently a plugin that can do this?
#Myke: you helped me tremendously. Here's my humble addition:
add_filter('json_api_encode', 'json_api_encode_acf');
function json_api_encode_acf($response)
{
if (isset($response['posts'])) {
foreach ($response['posts'] as $post) {
json_api_add_acf($post); // Add specs to each post
}
}
else if (isset($response['post'])) {
json_api_add_acf($response['post']); // Add a specs property
}
return $response;
}
function json_api_add_acf(&$post)
{
$post->acf = get_fields($post->id);
}
Update for Wordpress 4.7
With the release of Wordpress 4.7 the REST functionality is no longer provided as a distinct plugin, rather its rolled in (no plugin required).
The previous filters don't appear to work. However the following snippet does (can be in your functions.php):
>= PHP 5.3
add_filter('rest_prepare_post', function($response) {
$response->data['acf'] = get_fields($response->data['id']);
return $response;
});
< PHP 5.3
add_filter('rest_prepare_post', 'append_acf');
function append_acf($response) {
$response->data['acf'] = get_fields($response->data['id']);
return $response;
};
Note the filter is a wild card filter, applied like
apply_filters("rest_prepare_$type", ...
so if you have multiple content types (custom), you will need to do:
add_filter('rest_prepare_multiple_choice', 'append_acf');
add_filter('rest_prepare_vocabularies', 'append_acf');
function append_acf($response) {
$response->data['acf'] = get_fields($response->data['id']);
return $response;
};
Note It appears that rest_prepare_x is called per record. So if you are pinging the index endpoint, it will be called multiple times (so you don't need to check if its posts or post)
Came here by searching with the same question. This isn't totally vetted yet but I think this is getting on the right path. Check it out.
I have one less nested level than you do so this might need altered a bit. But the JSON API plugin has a filter called json_api_encode. I have a repeater called specifications that looks like this.
http://d.pr/i/YMvv
In my functions file I have this.
add_filter('json_api_encode', 'my_encode_specs');
function my_encode_specs($response) {
if (isset($response['posts'])) {
foreach ($response['posts'] as $post) {
my_add_specs($post); // Add specs to each post
}
} else if (isset($response['post'])) {
my_add_specs($response['post']); // Add a specs property
}
return $response;
}
function my_add_specs(&$post) {
$post->specs = get_field('specifications', $post->id);
}
Which appends a custom value to the JSON API output. Notice the get_field function from ACF works perfectly here for bringing back the array of the repeater values.
Hope this helps!
There is now a small plugin which adds the filter for you.
https://github.com/PanManAms/WP-JSON-API-ACF
I'm not sure if you're still interested in a solution, but I was able to modify the json-api plugin models/post.php file to display the repeater data as an array. This is a modification of a modification made by http://wordpress-problem.com/marioario-on-plugin-json-api-fixed-get-all-custom-fields-the-right-way/
replace the set_custom_fields_value() function with the following:
function set_custom_fields_value() {
global $json_api;
if ($json_api->include_value('custom_fields') && $json_api->query->custom_fields) {
// Query string params for this query var
$params = trim($json_api->query->custom_fields);
// Get all custom fields if true|all|* is passed
if ($params === "*" || $params === "true" || $params === "all") {
$wp_custom_fields = get_post_custom($this->id);
$this->custom_fields = new stdClass();
// Loop through our custom fields and place on property
foreach($wp_custom_fields as $key => $val) {
if (get_field($key)) {
$this->custom_fields->$key = get_field($key);
} else if ($val) {
// Some fields are stored as serialized arrays.
// This method does not support multidimensionals...
// but didn't see anything wrong with this approach
$current_custom_field = #unserialize($wp_custom_fields[$key][0]);
if (is_array($current_custom_field)) {
// Loop through the unserialized array
foreach($current_custom_field as $sub_key => $sub_val) {
// Lets append these for correct JSON output
$this->custom_fields->$key->$sub_key = $sub_val;
}
} else {
// Break this value of this custom field out of its array
// and place it on the stack like usual
$this->custom_fields->$key = $wp_custom_fields[$key][0];
}
}
}
} else {
// Well this is the old way but with the unserialized array fix
$params = explode(',', $params);
$wp_custom_fields = get_post_custom($this->id);
$this->custom_fields = new stdClass();
foreach ($params as $key) {
if (isset($wp_custom_fields[$key]) && $wp_custom_fields[$key][0] ) {
$current_custom_field = #unserialize($wp_custom_fields[$key][0]);
if (is_array($current_custom_field)) {
foreach($current_custom_field as $sub_key => $sub_val) {
$this->custom_fields->$key->$sub_key = $sub_val;
}
} else {
$this->custom_fields->$key = $wp_custom_fields[$key][0];
}
}
}
}
} else {
unset($this->custom_fields);
}
}
Current version of ACF prints out a custom_fields object on a call to the JSON API, containing all the fields relative to the Post or Page. I edited #Myke version to add specific custom fields from the ACF Option page to each Post or Page. Unfortunately there is not get_fields() function for the whole Option Page so you'll have to edit it depending on your fields structure.
add_filter('json_api_encode', 'json_api_encode_acf');
function json_api_encode_acf($response) {
if (isset($response['posts'])) {
foreach ($response['posts'] as $post) {
json_api_add_acf($post); // Add specs to each post
}
}
else if (isset($response['post'])) {
json_api_add_acf($response['post']); // Add a specs property
}
else if (isset($response['page'])) {
json_api_add_acf($response['page']); // Add a specs to a page
}
return $response;
}
function json_api_add_acf(&$post) {
$post->custom_fields->NAME_OF_YOUR_CUSTOM_FIELD = get_field( 'NAME_OF_YOUR_CUSTOM_FIELD', 'option' );
}

Magento: Disable module for any particular store

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/

MediaWiki Extension question / suggestion

Complete beginner here. I want to create a new tab on each page that has a custom action. When clicked, it takes you to a new page which has custom HTML on it along with the text or the original article.
So far I could create a new Tab and could give a custom action mycustomaction to it. I am pasting what I did so far here. Please let me know if I am using the correct hooks etc. and what is a better way to achieve this basic functionality.
So far with their docs I have done this:
#Hook for Tab
$wgHooks['SkinTemplateContentActions'][] = 'myTab';
#Callback
function myTab( $content_actions) {
global $wgTitle;
$content_actions['0'] = array(
'text' => 'my custom label',
'href' => $wgTitle->getFullURL( 'action=mycustomaction' ),
);
return true;
}
#new action hook
$wgHooks['UnknownAction'][] = 'mycustomaction';
#callback
function mycustomaction($action, $article) {
echo $action;
return true;
}
This gives me error:
No such action
The action specified by the URL is invalid. You might have mistyped the URL, or followed an incorrect link. This might also indicate a bug in the software used by yourplugin
What I was doing wrong:
$content_actions[‘0’] should simply be $content_actions[] (minor nitpick)
$content_actions is passed-by-reference, it should be function myTab( &$content_actions ) {}
mycustomaction() should do something along the lines of
if ( $action == ‘mycustomaction’ ) {
do stuff; return false;
}
else {
return true;
}
It should use $wgOut->addHTML() instead of echo
Thanks a lot everyone for your help!