Im trying to display the last accessed field when a user logs in. For this I tried using User::getLastLoginTime() . But this returns a fatal error saying
PHP Fatal error: Call to undefined method Drupal\customize_block\Plugin\Block\ContractDetails::get() in /var/www/html/core/modules/user/src/Entity/User.php on line 265
Here is my block ,in my custom module:
/**
* Provides a 'Customized contract' Block
*
* #Block(
* id = "customer_info",
* label = #Translation("Customer and contract info"),
* module = "user",
* context = {
* "current_user" = #ContextDefinition("entity:user", label = #Translation("Current User"))
* }
* )
namespace Drupal\customize_block\Plugin\Block;
use Drupal\Core\Block\BlockBase;
class ContractInfo extends BlockBase {
public function build() {
$output='';
global $base_url;
$current_user = $this->getContext('current_user');
$output.='<div>' . $current_user -> contextData . '</div>';
return array(
'#markup' => $output,
'#cache' => array(
'contexts' => array('url'),
),
);
}
}
Am I calling this method correct way?
No, you do not. You need some specific user, for example the current user or some other user object. To do it for the current user, it looks like this:
\Drupal::currentUser()->getLastAccessedTime();
Related
I'm using Zend Framework description and I'm trying to customize title tag like ZF2 documetation recommends, but I've got this error:
Uncaught Zend\ServiceManager\Exception\ServiceNotFoundException:
Unable to resolve service "viewHelperManager" to a factory; are you
certain you provided it during configuration? in
/var/www/html/basketmetrics/vendor/zendframework/zend-servicemanager/src/ServiceManager.php:681
Stack trace: #0
/var/www/html/basketmetrics/vendor/zendframework/zend-servicemanager/src/ServiceManager.php(757):
Zend\ServiceManager\ServiceManager->getFactory('viewHelperManag...')
1 /var/www/html/basketmetrics/vendor/zendframework/zend-servicemanager/src/ServiceManager.php(200):
Zend\ServiceManager\ServiceManager->doCreate('viewHelperManag...') #2
/var/www/html/basketmetrics/module/Stats/src/Module.php(43):
Zend\ServiceManager\ServiceManager->get('viewHelperManag...') #3
/var/www/html/basketmetrics/vendor/zendframework/zend-eventmanager/src/EventManager.php(322):
Stats\Module->setLayoutTitle(Object(Zend\Mvc\MvcEvent)) #4
/var/www/html/basketmetrics/vendor/zendframework/zend-eventmanager/src/EventManager.php(171):
Zend\EventManager\E in
/var/www/html/basketmetrics/vendor/zendframework/zend-servicemanager/src/ServiceManager.php
on line 681
This is my code on Module.php
namespace Stats;
class Module
{
const VERSION = '3.0.2';
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
/**
* #param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* #return void
*/
public function onBootstrap($e)
{
// Register a render event
$app = $e->getParam('application');
$app->getEventManager()->attach('render', array($this, 'setLayoutTitle'));
}
/**
* #param \Zend\Mvc\MvcEvent $e The MvcEvent instance
* #return void
*/
public function setLayoutTitle($e)
{
$matches = $e->getRouteMatch();
$action = $matches->getParam('action');
$controller = $matches->getParam('controller');
$module = __NAMESPACE__;
$siteName = 'BasketMetrics';
// Getting the view helper manager from the application service manager
$viewHelperManager = $e->getApplication()->getServiceManager()->get('viewHelperManager');
// Getting the headTitle helper from the view helper manager
$headTitleHelper = $viewHelperManager->get('headTitle');
// Setting a separator string for segments
$headTitleHelper->setSeparator(' - ');
// Setting the action, controller, module and site name as title segments
$headTitleHelper->append($action);
$headTitleHelper->append($controller);
$headTitleHelper->append($module);
$headTitleHelper->append($siteName);
}
}
I don't know If I'm doing something wrong, or if I cannot do it like in ZF2.
And meta tag "description" how can I customize it?
->get('ViewHelperManager')
Up case
I have configured slim to write logs to log files as the standard way. But this is not effective when we want to search large and all the logs at a given time. So I want to write those logs to a separate sqlite DB.
My question is how can I set the log writer to write the messages (as done in the Zend framework) ?
P S: I know that I can create a PDO object and use the queries. But I don't want to change the existing code. Just prefer to set the writer and let the framework do the job for me.
I managed to do this as follows,
Create the sqlite connection
$sqlite = new PDO('sqlite:./logs/log.db');
Create my own LogWritter similar to the framework
<?php
/**
* Description of LogWritter
*
* #author Ruwantha.Lankathilaka
*/
class LogWritter {
protected $sqliteConnection;
public function __construct($connection) {
$this->sqliteConnection = $connection;
}
/**
* Write function will bypass the slim default LogWriter and will return
* last inserted log id which could be used as a reference
*
* #param type $object will get the error message
* #param type $level will get the error levels of \Slim\Log
* #return mix if successfully logged will return the last insert id, else
* will return false
*/
public function write($object,$level) {
//Determine label
$label = 'DEBUG';
$message = (string) $object;
switch ($level) {
case \Slim\Log::FATAL:
$label = 'FATAL';
break;
case \Slim\Log::ERROR:
$label = 'ERROR';
break;
case \Slim\Log::WARN:
$label = 'WARN';
break;
case \Slim\Log::INFO:
$label = 'INFO';
break;
}
$sqliteQuery = "INSERT INTO logs (lable,message) VALUES (:lable,:message)";
$statement = $this->sqliteConnection->prepare($sqliteQuery);
$result = $statement->execute(array(':lable'=>$label,':message'=>$message));
if(!empty($result)){
return $this->sqliteConnection->lastInsertId();
}else{
return false;
}
}
}
Add the LogWritter to the index
Add the LogWritter to the Slim app
$app = new \Slim\Slim(array(
'log.writer' => $logWriter,
'log.enabled' => true,
'log.level' => \Slim\Log::DEBUG,
'debug' => true
));
now you can get the log from app
$retult = $app->log->error('test error');
$result will have the inserted log id false if the log failed
Hope this will help someone in future.
Hi I have the following drupal module
/**
* Implementation of hook_menu_alter().
*
* #param array $items
* Menu items keyed by path.
*/
function ajax_privacy_menu_alter(&$items) {
$items['node/%']['access callback'] = 'check_access';
$items['node/%']['access arguments'] = array(1);
}
function check_access($node_id)
{
if($node_id!=29)
return TRUE;
else
return FALSE;
}
If i type node/29 it returns access denied message as expected
but for other nodes ( eg: node/24 ) I get the following error
Notice: Object of class stdClass could not be converted to int in
check_access() (line 19 of
/home/pagergbr/public_html/pagerail/sites/all/modules/ajax_privacy/ajax_privacy.module).
and all the nodes in my site show up along with node/24. Please help
Your access callback gets $node object as an argument, try this:
function check_access($node) {
if ($node->nid != 29) {
...
}
}
I would like to run a duplicate content check just before firing off an email whcih uses swiftmailer inside my symph2 app to send me dev ERROR log entries.
this functionality sits right next to my error log to database function, where it too has a duplicate check, although that one is much easier, it uses sql.
for this one, i want to maintain the last mail sent body for atleast the next 10 emails sent, so that if my error log goes out of control, it wont keep firing me duplicate emails of the same error.
should i just collect this body onto an object that holds last 10 email bodies, and attach this to the swift mailer class? or is there an easier way, like using something that is already embedded in swift mailer for this kind of post sending use? Or maybe a session..
Edit, i call swift mailer from a backend helper class, so think i can pretty much do anything there so long as its atleast semi-elegant.
EDIT this is a refined version of the method that calls both the persist and firing of email
<?php
class someWierdClass
{
public function addLogAction(Request $request, $persist = TRUE, $addEmail = TRUE)
{
$responseAdd = array();
if ($this->getRequest()->request->all() !== null) {
$data = $this->getRequest()->request->get('data') ? $this->getRequest()->request->get('data') : 'no_data';
$duplicate = $this->getRequest()->request->get('duplicate', null);
}
if ($addEmail) {
$responseAdd[] = 'firedIt';
$this->fireEmailString('You have an error log here. <br>' . $data);
}
if ($persist)
{
$responseAdd[] = 'persistedIt';
$this->persistLog($data, $duplicate);
}
if ($responseAdd)
{
$body = implode(', ', $responseAdd);
return new Response($body);
}
}
}
Log emails in a table and check that there it isn't a duplicate every time you send an email.
To do this, you should create a helper function that queries the emails table for entries who's body matches the body you would like to send. If the query returns nothing, then you know that isn't a duplicate. You would then send the email and log it the database. Otherwise, if it returned (a) record(s), you would send a dev ERROR log entry.
If you would like to only check against the last 10 emails, you would do this by querying for both $body == $new_body and $id >= ($total_rows-10)
You would then inject this into the container and call it using something like this
$this->container->get('helper')->sendEmail($body, $subject, $recipients);
Ok, thanks Dan for the idea as to using the database to do the dup check. If you notice, per your suggestion, i was already doing the dup check, but it made me think. It helped me connect the dots.
What i have done is return the answer if its a duplicate on the response when it does the updating the database, then using that response as a flag to determine if email fires or not. (in my case, i go further to check the updated stamp is at least +1 hour old, as opposed to the 'last 10 emails content' idea)
Heres the code.. Enjoy..
<?php
namespace Acme\AcmeBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller,
Acme\AcmeBundle\Entity\Log,
Symfony\Component\HttpFoundation\JsonResponse,
Symfony\Component\HttpFoundation\Response,
Symfony\Component\Config\Definition\Exception\Exception,
Symfony\Component\HttpFoundation\Request,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
class someWierdClass
{
/**
* #var array
*/
protected $senderArray = array('no-reply#yorudomain.com' => 'Your Website Name');
/**
* #param Request $request
* #param bool $persist
* #param bool $addEmail
* #return Response
*/
public function addLogAction(Request $request, $persist = TRUE, $addEmail = TRUE)
{
$responseAdd = array();
if ($this->getRequest()->request->all() !== null) {
$data = $this->getRequest()->request->get('data') ? $this->getRequest()->request->get('data') : 'no_data';
$type = $this->getRequest()->request->get('type') ? $this->getRequest()->request->get('type') : 'no_type';
$duplicate = $this->getRequest()->request->get('duplicate', null);
}
if ($addEmail) {
$responseAdd[] = 'firedIt';
$this->fireEmailString('You have an error log here. <br>' . $data);
}
if ($persist) {
$responseAdd[] = 'persistedIt';
$persistResponse = $this->persistLog( $type = $data, $duplicate);
if ($persistResponse) {
// a dup check is done here and results of this is on the response. (e.g. $content->passesCutoff)
$content = json_decode($persistResponse->getContent());
}
}
if ( $addEmail && ( isset($content->passesCutoff) && $content->passesCutoff ))
{
//fire off an email also, because its kind of hard to look in teh logs all the time, sometimes we just want an email.
$successEmail = $this->fireEmailString($data);
if( ! $successEmail )
{
$responseAdd[] = 'firedIt';
}
}
if ($responseAdd) {
$body = implode(', ', $responseAdd);
return new Response($body);
}
}
/**
* #param $emailStringData
* #param null $emailSubject
* #param null $emailTo
* #return mixed
*/
protected function fireEmailString($emailStringData, $emailSubject = null, $emailTo=null){
$templateName = 'AcmeBundle:Default:fireEmailString.html.twig';
if( ! $emailSubject )
{
$emailSubject = 'An email is being fired to you!' ;
}
if( ! $emailTo )
{
$emailTo = 'youremail#gmail.com';
}
$renderedView = $this->renderView(
$templateName, array(
'body' => $emailStringData,
));
$mailer = $this->get('mailer');
$message = $mailer->createMessage()
->setSubject( $emailSubject)
->setBody($emailStringData, 'text/plain')
->addPart($renderedView, 'text/html')
->setFrom($this->senderArray)
->setSender($this->senderArray)
->setTo($emailTo);
$results = $mailer->send($message);
return $results;
}
/**
* #param $type
* #param $data
* #param $duplicate
* #return JsonResponse
*/
protected function persistLog($type, $data, $duplicate) {
$em = $this->getDoctrine()->getManager();
$count = null;
$passesCutoff = null;
$mysqlNow = new \DateTime(date('Y-m-d G:i:s'));
//only two conditions can satisy here, strings '1' and 'true'.
if($duplicate !== '1' && $duplicate !== 'true' /*&& $duplicate != TRUE*/)
{
//in order to check if its unique we need to get the repo
//returns an object (findByData() would return an array)
$existingLog = $em->getRepository('AcmeBundle:Log')->findOneByData(
array('type' => $type, 'data' => $data)
);
if($existingLog)
{
$timeUpdatedString = strtotime($existingLog->getTimeupdated()->format('Y-m-d H:i:s'));
$cutoffStamp = strtotime('+1 hour', $timeUpdatedString); //advance 1 hour (customize this to the amount of time you want to go by before you consider this a duplicate. i think 1 hour is good)
$passesCutoff = time() >= $cutoffStamp ? TRUE : FALSE; //1 hour later
$count = $existingLog->getUpdatedcount();
$existingLog->setUpdatedcount($count + 1); // '2014-10-11 03:52:20' // date('Y-m-d G:i:s')
$em->persist($existingLog);
}
else
{
//this record isnt found, must be unique
$newLog = new Log(); //load our entity
//set in new values
$newLog->setType($type);
$newLog->setData($data);
$newLog->setUpdatedcount(0);
$newLog->setTimeupdated($mysqlNow);
$em->persist($newLog);
}
}
else
{
//we dont care if unique or not, we just want a new row
$newLog = new Log(); //load our entity
$newLog->setType($type);
$newLog->setData($data);
//time updated has been set to auto update to current timestamp in the schema, test first, then remove this
$newLog->setUpdatedcount(0);
$newLog->setTimeupdated($mysqlNow);
$em->persist($newLog);
}
$em->flush();
$response = new JsonResponse();
$response->setData(
array(
'data' => 'persistedIt',
'existingLog' => $count,
'passesCutoff' => $passesCutoff,
));
return $response;
}
}
In hindsight, i would have just passed the last update timestamp back on the response from the persist method, then do the cutoff calculation inside the fire email method obviously, but the above code does work as a starting point.. :-)
I asked this question and found out that we can't get the error message thrown by a DataTransformer (according to the only user who answered, maybe it's possible, I don't know).
Anyway, now that I know that, I am stucked with a problem of validation. Suppose my model is this one: I have threads that contains several participants (users).
<?php
class Thread
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="My\UserBundle\Entity\User")
* #ORM\JoinTable(name="messaging_thread_user")
*/
private $participants;
// other fields, getters, setters, etc
}
For thread creation, I want the user to specify the participants usernames in a textarea, separated by "\n".
And I want that if one or more of the usernames specified don't exist, a message is displayed with the usernames that don't exist.
For example, "Users titi, tata and toto don't exist".
For that I created a DataTransformer that transforms the raw text in the textarea into an ArrayCollection containing instances of users. Since I can't get the error message provided by this DataTransformer (such a shame! Is it really impossible?), I don't check the existence of each usernames in the DataTransformer but in the Validator.
Here is the DataTransformer that converts \n-separated user list into an ArrayCollection (so that the DataBinding is ok):
<?php
public function reverseTransform($val)
{
if (empty($val)) {
return null;
}
$return = new ArrayCollection();
// Extract usernames in an array from the raw text
$val = str_replace("\r\n", "\n", trim($val));
$usernames = explode("\n", $val);
array_map('trim', $usernames);
foreach ($usernames as $username) {
$user = new User();
$user->setUsername($username);
if (!$return->contains($user)) {
$return->add($user);
}
}
return $return;
}
And here is my validator:
<?php
public function isValid($value, Constraint $constraint)
{
$repo = $this->em->getRepository('MyUserBundle:User');
$notValidUsernames = array();
foreach ($value as $user) {
$username = $user->getUsername();
if (!($user = $repo->findOneByUsername($username))) {
$notValidUsernames[] = $username;
}
}
if (count($notValidUsernames) == 0) {
return true;
}
// At least one username is not ok here
// Create the list of usernames separated by commas
$list = '';
$i = 1;
foreach ($notValidUsernames as $username) {
if ($i < count($notValidUsernames)) {
$list .= $username;
if ($i < count($notValidUsernames) - 1) {
$list .= ', ';
}
}
$i++;
}
$this->setMessage(
$this->translator->transChoice(
'form.error.participant_not_found',
count($notValidUsernames),
array(
'%usernames%' => $list,
'%last_username%' => end($notValidUsernames)
)
)
);
return false;
}
This current implementation looks ugly. I can see the error message well, but the users in the ArrayCollection returned by the DataTransformer are not synchronized with Doctrine.
I got two questions:
Is there any way that my validator could modify the value given in parameter? So that I can replace the simple User instances in the ArrayCollection returned by the DataTransformer into instances retrieved from the database?
Is there a simple and elegant way to do what I'm doing?
I guess the most simple way to do this is to be able to get the error message given by the DataTransformer. In the cookbook, they throw this exception: throw new TransformationFailedException(sprintf('An issue with number %s does not exist!', $val));, if I could put the list of non-existing usernames in the error message, it would be cool.
Thanks!
I am the one that answered your previous thread so maybe someone else will jump in here.
Your code can be simplified considerably. You are only dealing with user names. No need for use objects or array collections.
public function reverseTransform($val)
{
if (empty($val)) { return null; }
// Extract usernames in an array from the raw text
// $val = str_replace("\r\n", "\n", trim($val));
$usernames = explode("\n", $val);
array_map('trim', $usernames);
// No real need to check for dups here
return $usernames;
}
The validator:
public function isValid($userNames, Constraint $constraint)
{
$repo = $this->em->getRepository('SkepinUserBundle:User');
$notValidUsernames = array();
foreach ($userNames as $userName)
{
if (!($user = $repo->findOneByUsername($username)))
{
$notValidUsernames[$userName] = $userName; // Takes care of dups
}
}
if (count($notValidUsernames) == 0) {
return true;
}
// At least one username is not ok here
$invalidNames = implode(' ,',$notValidUsernames);
$this->setMessage(
$this->translator->transChoice(
'form.error.participant_not_found',
count($notValidUsernames),
array(
'%usernames%' => $invalidNames,
'%last_username%' => end($notValidUsernames)
)
)
);
return false;
}
=========================================================================
So at this point
We have used transformer to copy the data from the text area and generated an array of user names during form->bind().
We then used a validator to confirm that each user name actually exists in the database. If there are any that don't then we generate an error message and form->isValid() will fail.
So now we are back in the controller, we know we have a list of valid user names (possibly comma delimited or possibly just an array). Now we want to add these to our thread object.
One way would to create a thread manager service and add this functionality to it. So in the controller we might have:
$threadManager = $this->get('thread.manager');
$threadManager->addUsersToThread($thread,$users);
For the thread manager we would inject our entity manager. In the add users method we would get a reference to each of the users, verify that the thread does not already have a link to this user, call $thread->addUser() and then flush.
The fact that we have wrapped up this sort of functionality into a service class will make things easier to test as we can also make a command object and run this from the command line. it also gives us a nice spot to add additional thread related functionality. We might even consider injecting this manager into the user name validator and moving some of the isValid code to the manager.