So, this is my first encounter with SuiteCRM or any other CRM for that matter. I need to query the db on a table that is not used by the CRM for our quote system. So, I have created the module using module builder and modified the module file so that it uses the correct table. The problem is that when the query runs, SuiteCRM still adds its default where clauses and adds the deleted = 0 condition to the query.
So, I tried using the method that is described on this SO page. That doesn't work as I get the an error that I am using an undefined variable (db) and that I am calling to a member function fetchByAssoc() on a non-object. Now, I am placing the code in the moduleName.php file. Maybe that is my issue. I don't know as I have never worked on any other CRM project. If anyone can point me in the right direction as to what I will need to do to be able to query a different table other than the default CRM table and then show the results from that query inside of a dashlet, your help will be greatly appreciated.
I got the errors fixed. They were my fault as I had not referenced the object.
So, as requested, here is some of my code. This is my php file.
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('include/Dashlets/Dashlet.php');
class FrtwQuotesDashlet extends Dashlet {
var $height = '200'; // height of the dashlet
var $quoteData = "";
/**
* Constructor
*
* #global string current language
* #param guid $id id for the current dashlet (assigned from Home module)
* #param array $def options saved for this dashlet
*/
function FrtwQuotesDashlet($id, $def) {
$this->loadLanguage('FrtwQuotesDashlet');
if(!empty($def['height'])) // set a default height if none is set
$this->height = $def['height'];
parent::Dashlet($id); // call parent constructor
$this->isConfigurable = true; // dashlet is configurable
$this->hasScript = false; // dashlet has javascript attached to it
// if no custom title, use default
if(empty($def['title'])) $this->title = $this->dashletStrings['LBL_TITLE'];
else $this->title = $def['title'];
}
/**
* Displays the dashlet
*
* #return string html to display dashlet
*/
function display() {
$sql = "SELECT QuoteNbr, crmname, ShipToCity FROM quotes.quotehdr LIMIT 10";
$result = $GLOBALS["db"]->query($sql);
$quoteData = "<table>";
while($quotes = $GLOBALS["db"]->fetchByAssoc($result)){
foreach ($quotes as $quote) {
$quoteData .="<tr><td>".$quote[0]."</td><td>".$quote[1]."</td><td>".$quote[2]."</td></tr>";
}
}
$ss = new Sugar_Smarty();
//assign variables
//$ss->assign('greeting', $this->dashletStrings['LBL_GREETING']);
$ss->assign('quoteData', $this->quoteData);
$ss->assign('height', $this->height);
$str = $ss->fetch('custom/modules/Home/FrtwQuotesDashlet/FrtwQuotesDashlet.tpl');
return parent::display().$str;
}
}
?>
The issue was with the foreach loop. I removed it and now it works fine. In analyzing the code, the while loop actually does the iterations needed. So, by adding the foreach, what was happening was that the code was iterating over each row returned from the db and then doing some weird stuff -- as in, it would only return a partial string of what each value should be. Since I am querying on 3 fields, it would also loop over each row 3 times, thereby creating 3 different rows from each row. So, for anyone with similar issue, this is how working code looks.
<?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
require_once('include/Dashlets/Dashlet.php');
class FrtwQuotesDashlet extends Dashlet {
var $height = '200'; // height of the dashlet
var $quoteData = "";
/**
* Constructor
*
* #global string current language
* #param guid $id id for the current dashlet (assigned from Home module)
* #param array $def options saved for this dashlet
*/
function FrtwQuotesDashlet($id, $def) {
$this->loadLanguage('FrtwQuotesDashlet');
if(!empty($def['height']))
$this->height = $def['height'];
parent::Dashlet($id);
$this->isConfigurable = true;
$this->hasScript = false;
// if no custom title, use default
if(empty($def['title'])) $this->title = $this->dashletStrings['LBL_TITLE'];
else $this->title = $def['title'];
}
/**
* Displays the dashlet
*
* #return string html to display dashlet
*/
function display() {
$sql = "SELECT QuoteNbr, revnbr, crmname, ShipToCity FROM quotes.quotehdr LIMIT 10";
$result = $GLOBALS["db"]->query($sql);
$this->quoteData = "Need headers here when we determine exact fields....";
while($quotes = $GLOBALS["db"]->fetchByAssoc($result)){
$this->quoteData .="<tr><td width = \"30%\">".$quotes["QuoteNbr"].' '.$quotes['revnbr']."</td><td width = \"30%\">".$quotes["crmname"]."</td><td width = \"30%\">".$quotes["ShipToCity"]."</td></tr>";
}
$ss = new Sugar_Smarty();
//assign variables
// $ss->assign('greeting', $this->dashletStrings['LBL_GREETING']);
$ss->assign('greeting', "This is the Greeting....");
$ss->assign('quoteData', $this->quoteData);
$ss->assign('height', $this->height);
$str = $ss->fetch('modules/Home/Dashlets/FrtwQuotesDashlet/FrtwQuotesDashlet.tpl');
return parent::display().$str; // return parent::display for title and such
}
}
?>
Related
I'm building the admin for a Magento2 store (currently on 2.1.7, they want to use the newest version until we go live and then want to stabilize a particular version). The module in question is supposed to display all existing orders, with an actionsColumn that contains links to cancel, edit, and open a detailed overview of the purchased items associated with that order. The order detail page contains a grid view that should display all order items associated with an order number passed in the URL.
In order to filter out Order Items that don't relate to the specific Order Number, I've extended the \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult class. This works except for one weird caveat. If, in the addFieldToFilter call, I replace $ordNum with, say, '10000', it grabs the correct data. When using $ordNum to call this dynamically, however, it returns no rows at all. This despite trying all sorts of casting and === checks to ensure that there's no difference between the hardcoded and dynamic values. Is this a Magento bug? I can't at all figure out why this would be the case.
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$ordNum = $request->getParam('order_num');
$this->addFieldToFilter('order_num', ['eq' => $ordNum]); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
I just fixed it by using mentioned below steps
Store param value in session in controller
public function execute() {
$this->_catalogSession->setTokenId($this->request->getParam('entity_id'));
$this->_view->loadLayout();
$this->_view->loadLayoutUpdates();
$this->_view->getPage()->getConfig()->getTitle()->set(__('Redeem Token History'));
$this->_view->renderLayout();
}
Use session value in dataprovider
$tokensCollection->addFieldToFilter('token_id', ['eq' => $this->_catalogSession->getTokenId()]);
Enjoy :)
Try this in place of the getParam statement:
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
Just to make sure we are on the same page, this is the full code:
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
$this->addFieldToFilter('order_num', $ordNum); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
We have solved this issue by doing the following :
/**
* CcCustompriceProductListingDataProvider constructor.
* #param string $name
* #param string $primaryFieldName
* #param string $requestFieldName
* #param \Magento\Framework\Api\Search\ReportingInterface $reporting
* #param \Magento\Framework\Api\Search\SearchCriteriaBuilder $searchCriteriaBuilder
* #param \Magento\Framework\App\RequestInterface $request
* #param \Magento\Framework\Api\FilterBuilder $filterBuilder
* #param array $meta
* #param array $data
* #throws \Exception
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
ReportingInterface $reporting,
SearchCriteriaBuilder $searchCriteriaBuilder,
RequestInterface $request,
FilterBuilder $filterBuilder,
array $meta = [],
array $data = []
) {
$data['config']['filter_url_params']['product_id'] = $request->getParam('cppc_product_id', 0);
parent::__construct($name, $primaryFieldName, $requestFieldName, $reporting, $searchCriteriaBuilder, $request, $filterBuilder, $meta, $data);
}
You do not need to use any other function. The reason why this is is because it is also updated with an update URL and that does not have that parameter. By using adding that to the data it also parses that into the update url.
You can see that here (Parent function)
/**
* #return void
*/
protected function prepareUpdateUrl()
{
if (!isset($this->data['config']['filter_url_params'])) {
return;
}
foreach ($this->data['config']['filter_url_params'] as $paramName => $paramValue) {
if ('*' == $paramValue) {
$paramValue = $this->request->getParam($paramName);
}
if ($paramValue) {
$this->data['config']['update_url'] = sprintf(
'%s%s/%s/',
$this->data['config']['update_url'],
$paramName,
$paramValue
);
$this->addFilter(
$this->filterBuilder->setField($paramName)->setValue($paramValue)->setConditionType('eq')->create()
);
}
}
}
So I've been trying to get data from a single item from the database using the MVC method in Joomla. I've been inspecting com_content how to do this, but I can't seem to get the id from the URL
This is my model to get the data for a single item
<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import Joomla modelitem library
jimport('joomla.application.component.modelitem');
/**
* Issuu Model
*/
class IssuuModelItem extends JModelItem
{
/**
* #var string msg
*/
protected $item;
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* #since 1.6
*
* #return void
*/
protected function populateState()
{
$app = JFactory::getApplication('site');
// Load state from the request.
$id = $app->input->getInt('id');
$this->setState('item.id', $id);
$offset = $app->input->getUInt('limitstart');
$this->setState('list.offset', $offset);
// Load the parameters.
$params = $app->getParams();
$this->setState('params', $params);
// TODO: Tune these values based on other permissions.
$user = JFactory::getUser();
if ((!$user->authorise('core.edit.state', 'com_issuu')) && (!$user->authorise('core.edit', 'com_issuu')))
{
$this->setState('filter.published', 1);
$this->setState('filter.archived', 2);
}
$this->setState('filter.language', JLanguageMultilang::isEnabled());
}
/**
* Returns a reference to the a Table object, always creating it.
*
* #param type The table type to instantiate
* #param string A prefix for the table class name. Optional.
* #param array Configuration array for model. Optional.
* #return JTable A database object
* #since 2.5
*/
public function getTable($type = 'Item', $prefix= 'IssuuTable', $config = array()) {
return JTable::getInstance($type,$prefix,$config);
}
/**
* Get the message
* #return string The message to be displayed to the user
*/
public function getItem($id = null)
{
$id = (!empty($id)) ? $id : (int) $this->getState('item.id');
if(!is_array($this->item)) {
$this->item = array();
}
// Get a db connection.
$db = JFactory::getDbo();
// Create a new query object.
$query = $db->getQuery(true);
// Select all records from the user profile table where key begins with "custom.".
// Order it by the ordering field.
$query->select($db->quoteName(array('id', 'title', 'username', 'docname', 'docid','date', 'pages', 'description')));
$query->from($db->quoteName('#__issuu_publications'));
$query->where($db->quoteName('state') . ' = `1` AND ' . $db->quoteName('id') . ' = ' . $db->qouteName($id));
$query->order('date ASC');
// Reset the query using our newly populated query object.
$db->setQuery($query);
// Load the results as a list of stdClass objects (see later for more options on retrieving data).
$this->items = $db->loadObjectList();
return $this->items;
}
}
I get a SQL error because the $id is empty.. (I removed the prefix from the table)
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ORDER BY date ASC' at line 4 SQL=SELECT `id`,`title`,`username`,`docname`,`docid`,`date`,`pages`,`description` FROM `#__issuu_publications` WHERE `state` = `1` AND `id` = ORDER BY date ASC
Any help is appreciated!
Try replacing your where clause with the following:
$query->where($db->quoteName('state') . ' = 1 AND ' . $db->quoteName('id') . ' = ' . $db->quote((int) $id));
I've removed the quotes around 1
replaced $id with (int) $id
And corrected a spelling mistake on quoteName
From what I understand, after reading documentation (especially scoring part), every field I add has the same level of importance when scoring searched results. I have following code:
protected static $_indexPath = 'tmp/search/indexes/projects';
public static function createSearchIndex()
{
$_index = new Zend_Search_Lucene(APPLICATION_PATH . self::$_indexPath, true);
$_projects_stmt = self::getProjectsStatement();
$_count = 0;
while ($row = $_projects_stmt->fetch()) {
$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::text('name', $row['name']));
$doc->addField(Zend_Search_Lucene_Field::text('description', $row['description']));
$doc->addField(Zend_Search_Lucene_Field::unIndexed('projectId', $row['id']));
$_index->addDocument($doc);
}
$_index->optimize();
$_index->commit();
}
The code is simple - I'm generating index, based on data fetched from db, and save it in the specified location.
I was looking in many places, as my desired behavior is that name field is more important than description (let's say 75% and 25%). So when I will search for some phrase, and it will be found in description of the first document, and in name of the second document, then second document will in fact have 3 times bigger score, and will show up higher on my list.
Is there any way to control scoring/ordering in this way?
I found it out basing on this documentation page. You need to create new Similarity algorithm class, and overwrite lengthNorm method. I copied this method from Default class, added $multiplier variable, and set it's value when needed (for a column I want):
class Zend_Search_Lucene_Search_Similarity_Projects extends Zend_Search_Lucene_Search_Similarity_Default
{
/**
* #param string $fieldName
* #param integer $numTerms
* #return float
*/
public function lengthNorm($fieldName, $numTerms)
{
if ($numTerms == 0) {
return 1E10;
}
$multiplier = 1;
if($fieldName == 'name') {
$multiplier = 3;
}
return 1.0/sqrt($numTerms / $multiplier);
}
}
Then the only thing you need to do (edit of code from question) is set your new Similarity algorithm class as a default method just before indexing:
protected static $_indexPath = 'tmp/search/indexes/projects';
public static function createSearchIndex()
{
Zend_Search_Lucene_Search_Similarity::setDefault(new Zend_Search_Lucene_Search_Similarity_Projects());
$_index = new Zend_Search_Lucene(APPLICATION_PATH . self::$_indexPath, true);
$_projects_stmt = self::getProjectsStatement();
$_count = 0;
while ($row = $_projects_stmt->fetch()) {
$doc = new Zend_Search_Lucene_Document();
$doc->addField(Zend_Search_Lucene_Field::text('name', $row['name']));
$doc->addField(Zend_Search_Lucene_Field::text('description', $row['description']));
$doc->addField(Zend_Search_Lucene_Field::unIndexed('projectId', $row['id']));
$_index->addDocument($doc);
}
$_index->optimize();
$_index->commit();
}
I wanted to extra boost name field, but you can do it with anyone.
I'm getting the following warning when try to view list_of_holidays.pdf from the remote server:
Warning (2): Cannot modify header information - headers already sent by (output started
at /home/aquinto1/app/views/helpers/flash.php:155) [APP/vendors/tcpdf/tcpdf.php, line 8541]
TCPDF ERROR: Some data has already been output to browser, can't send PDF file
line 155 is the last line in flash.php ie the closing tag for php (?>). Before that it is the code to embedSWF. I don't see anything wrong with that.
However, it is displaying fine on the local server.
I've checked for whitespaces and yet the error is still there.
i'm already using ob_clean before the output.
can someone tell me on what i'm doing wrong. FYI i'm using cakephp with tcpdf.
The following is flash.php
class FlashHelper extends AppHelper {
var $helpers = array('Javascript');
/**
* Used for remembering options from init() to each renderSwf
*
* #var array
*/
var $options = array(
'width' => 100,
'height' => 100
);
/**
* Used by renderSwf to set a flash version requirement
*
* #var string
*/
var $defaultVersionRequirement = '9.0.0';
/**
* Used by renderSwf to only call init if it hasnt been done, either
* manually or automatically by a former renderSwf()
*
* #var boolean
*/
var $initialized = false;
/**
* Optional initializing for setting default parameters and also includes the
* swf library. Should be called once, but if using several groups of flashes,
* MAY be called several times, once before each group.
*
* #example echo $flash->init();
* #example $flash->init(array('width'=>200,'height'=>100);
* #return mixed String if it was not able to add the script to the view, true if it was
*/
function init($options = array()) {
if (!empty($options)) {
$this->options = am($this->options, $options);
}
$this->initialized = true;
$view =& ClassRegistry::getObject('view');
if (is_object($view)) {
$view->addScript($this->Javascript->link('swfobject'));
return true;
} else {
return $this->Javascript->link('swfobject');
}
}
/**
* Wrapper for the SwfObject::embedSWF method in the vendor. This method will write a javascript code
* block that calls that javascript method. If given a dom id as fourth parameter the flash will
* replace that dom object. If false is given, a div will be placed at the point in the
* page that this method is echo'ed. The last parameter is mainly used for sending in extra settings to
* the embedding code, like parameters and attributes. It may also send in flashvars to the flash.
*
* For doucumentation on what options can be sent, look here:
* http://code.google.com/p/swfobject/wiki/documentation
*
* #example echo $flash->renderSwf('counter.swf'); // size set with init();
* #example echo $flash->renderSwf('flash/ad.swf',100,20);
* #example echo $flash->renderSwf('swf/banner.swf',800,200,'banner_ad',array('params'=>array('wmode'=>'opaque')));
* #param string $swfFile Filename (with paths relative to webroot)
* #param int $width if null, will use width set by FlashHelper::init()
* #param int $height if null, will use height set by FlashHelper::init()
* #param mixed $divDomId false or string : dom id
* #param array $options array('flashvars'=>array(),'params'=>array('wmode'=>'opaque'),'attributes'=>array());
* See SwfObject documentation for valid options
* #return string
*/
function renderSwf($swfFile, $width = null, $height = null, $divDomId = false, $options = array()) {
$options = am ($this->options, $options);
if (is_null($width)) {
$width = $options['width'];
}
if (is_null($height)) {
$height = $options['height'];
}
$ret = '';
if (!$this->initialized) {
$init = $this->init($options);
if (is_string($init)) {
$ret = $init;
}
$this->initialized = TRUE;
}
$flashvars = '{}';
$params = '{wmode : "opaque"}';
$attributes = '{}';
if (isset($options['flashvars'])) {
$flashvars = $this->Javascript->object($options['flashvars']);
}
if (isset($options['params'])) {
$params = $this->Javascript->object($options['params']);
}
if (isset($options['attributes'])) {
$attributes = $this->Javascript->object($options['attributes']);
}
if ($divDomId === false) {
$divDomId = uniqid('c_');
$ret .= '<div id="'.$divDomId.'"></div>';
}
if (isset($options['version'])) {
$version = $options['version'];
} else {
$version = $this->defaultVersionRequirement;
}
if (isset($options['install'])) {
$install = $options['install'];
} else {
$install = '';
}
$swfLocation = $this->webroot.$swfFile;
$ret .= $this->Javascript->codeBlock(
'swfobject.embedSWF
("'.$swfLocation.'", "'.$divDomId.'", "'.$width.'", "'.$height.'", "'.$version.'",
"'.$install.'", '.$flashvars.', '.$params.', '.$attributes.');');
return $ret;
}
}
?>
Simply do what the error tells you: Check app/views/helpers/flash.php line 155 and see what it is outputting there and fix it. There must be some code that outputs something.
Could it be one of the return statements?
if (is_object($view)) {
$view->addScript($this->Javascript->link('swfobject'));
return true;
} else {
return $this->Javascript->link('swfobject');
}
$ret .= $this->Javascript->codeBlock(
'swfobject.embedSWF
("'.$swfLocation.'", "'.$divDomId.'", "'.$width.'", "'.$height.'", "'.$version.'",
"'.$install.'", '.$flashvars.', '.$params.', '.$attributes.');');
return $ret;
}
What other code is on the page calling flash?
how moodle1.9 save user attempt quiz result in database and which tables are updated when any quiz has been attempted by user?
Please guide me.
If possible please updated me, which functions are used to insert user quiz attempted data in moodle1.9 database?
From the attempt.php file (Moodle 1.9.7):
$attempt = quiz_create_attempt($quiz, $attemptnumber);
Then:
if (!$attempt->id = insert_record('quiz_attempts', $attempt)) {
error('Could not create new attempt');
}
From locallib.php:
/**
* Creates an object to represent a new attempt at a quiz
*
* Creates an attempt object to represent an attempt at the quiz by the current
* user starting at the current time. The ->id field is not set. The object is
* NOT written to the database.
* #return object The newly created attempt object.
* #param object $quiz The quiz to create an attempt for.
* #param integer $attemptnumber The sequence number for the attempt.
*/
function quiz_create_attempt($quiz, $attemptnumber) {
global $USER, $CFG;
if (!$attemptnumber > 1 or !$quiz->attemptonlast or !$attempt = get_record('quiz_attempts', 'quiz', $quiz->id, 'userid', $USER->id, 'attempt', $attemptnumber-1)) {
// we are not building on last attempt so create a new attempt
$attempt->quiz = $quiz->id;
$attempt->userid = $USER->id;
$attempt->preview = 0;
if ($quiz->shufflequestions) {
$attempt->layout = quiz_repaginate($quiz->questions, $quiz->questionsperpage, true);
} else {
$attempt->layout = $quiz->questions;
}
}
$timenow = time();
$attempt->attempt = $attemptnumber;
$attempt->sumgrades = 0.0;
$attempt->timestart = $timenow;
$attempt->timefinish = 0;
$attempt->timemodified = $timenow;
$attempt->uniqueid = question_new_attempt_uniqueid();
return $attempt;
}
Please refer to the source code for major details.