I've made a basic Joomla module for my site as a shoutbox. But I'd like to put AJAX in it (I know a similar module with AJAX already exists on JED but this more a project for me to learn how AJAX works in a Joomla module).
The usual AJAX stuff where you redirect to a new php file obviously doesn't work as the file will not be defined as
defined('_JEXEC') or die('Restricted access');
will fail in a new page. And defining _JEXEC to be equal to one (as I've read in several posts on SO) as far as I've read on Joomla Docs is a security risk as it provides an entry point onto the site.
The way the other shoutbox module I've seen does is to point back at a function in the helper.php file. Which makes sense to me as that is where all the functions should normally be stored. However I'm unclear as to how to the module was accessing the helper.php file on a onSubmit() (or a related) command and was hoping someone could shed some light on this.
I don't actually need anything specific to my shoutbox module - this is more a question of how to get AJAX functionality in Joomla modules and how it is arranged
The other two were onto the right track you need to remember that when you make your AJAX call you can't directly access a php file in Joomla. So instead it's better to make calls to your module. In this case and have the module check for variables in your POST or in the URL.
I'm a big fan of JQuery's ajax it's a lot more self contained than the method the guy who built that shoutbox used.
$( "#addShout" ).click( function(event, ui) {
$.ajax({
type: 'GET',
url: "<?php echo JURI::base() . "index.php?option=mod_mymodule&task=getCustomerJson&selectedCustomer="?>" + encodeURIComponent(value),
success:function(data){
$('#shouts').append(data);
},
error:function(){
$('#errors').html('<p class="error"><strong>Oops!</strong> Try that again in a few moments.</p>');
}
});
});
Then as I mentioned in my comment to Valentin you:
$task = JRequest::getVar('task');
if($task == "getCustomerJson"){mySuperHelper::getCustomerJson();}
You only call the necessary functions when the variables exist.
To explain part of the process it's kinda like this:
If there are no variables in the POST or URL it will simply display the module the way Joomla expects.
If the variable are detected call the method to add it to the database and the javascript function that will add it the the display. Also prevent the normal display from happening.
The module you referenced was quite interesting. The helper functions that the main file referenced does what a model would normally handle in MVC and the Javascript was also kinda all over. If you really want to understand how that one worked you really need to dig into the fatAjax.js file as it contains all of the AJAX and sets the variables that the mod_shoutbox.php listens for.
Fron Joomla 3.2 if you needed to make an AJAX request in Joomla! from a custom module or plugin, you can build the URL as below.
index.php?option=com_ajax&module=your_module_name&method=YourMethodName&format=json
Explanation of each parameter
1. index.php?option=com_ajax : ALL requests must be routed through com_ajax.
2. module=your_module_name: The first part of this is the type of extension you're using. This can either be 'module' or 'plugin'. The second part is the name of the extension. please take care you're not including the prefix 'mod_' or 'plg_', just the name.
3. method=YourMethodName: This is the method name that you are trying to call. If you're using this in a module then the method must be appended with 'Ajax'. So, the method represented here will be YourMethodNameAjax. If you're using this on a plugin, the method must be prepended with 'onAjax'. So, the method represented here will be onAjaxYourMethodName.
Check more at Joomla doc
jQuery(document).ready(function () {
jQuery(".btnshow").click(function () {
var pagename, pid;
pid=jQuery(this).attr('rel');
pagename="<?php echo JURI::root();?>index.php?option=com_componentname&view=result&Id="+pid;
jQuery.get(pagename, function (data) {
jQuery('.para1').html(data);
});
});
});
<img src="<?php echo $image; ?>" alt="" />
From my knowledge you can't do AJAX calls from the module to a function in the module itself (or similar).
You can build a component and create a view which returns RAW or JSON as a response. Because Joomla! itself will be called you can use the Joomla! API & all the goodies from it. It's also a security thing, because if you need to get sensitive data, you can also do ACL / user checks.
When you can call from the module something like:
index.php?option=com_mycomponent&task=getJson
The article Generating JSON output is a good starting point.
You can achieve the desired result by the following way:
$( "#addShout" ).click( function(event, ui) {
$.ajax({
type: 'GET',
url: "<?php echo JURI::base() . "index.php?option=com_ajax&module=module_name_withot_prefix&method=methodName&anyparams="?>" + encodeURIComponent(anyparamsvalue) + "&format=json",,
success:function(data){
$('#shouts').append(data);
},
error:function(){
$('#errors').html('<p class="error"><strong>Oops!</strong> Try that again in a few moments.</p>');
}
});
});
In the module helper file, you'll need to have a method with name 'methodName'.
Related
i have create module using module builder , now i am having a field called as book Name
now if i give same book name 2 time t is accepting .
i don't want to use and plug in for checking duplicate value because i want to learn the customization through code .
so i can call ajax and check in data base weather the same book name is exist in db or not but i don't know how controller works in sugar crm . and how to call ajax in sugar crm .
can any one guide me , your help is much appreciated .
If you really want to accomplish this using ajax then I'd recommend an entryPoint as the way to go. This customization will require a couple of simple things. First you'll write a little bit of javascript to perform the actual ajax call. That ajax call will post to the entryPoint you write. The entryPoint will run the query for you and return a response to you in the edit view. So lets get started by writing the entryPoint first.
First, open the file custom/include/MVC/Controller/entry_point_registry.php. If the folder structure and file do not exist yet, go ahead and create them.
Add the following code to the entry_point_registry.php file:
$entry_point_registry['test'] = array('file' => 'custom/test.php', 'auth' => true);
Some quick explanation about that line:
The index value of test can be changed to whatever you like. Perhaps 'unique_book_value' makes more sense in your case. You'll see how this value is used in a minute.
The file value in the array points to where you're gonna put your actual code. You should also give this a more meaningful name. It does NOT need to match the array key mentioned above.
The 'auth' => true part determines whether or not the browser needs to have an active logged in session with SugarCRM or not. In this case (and almost all) I'd suggest keeping this to true.
Now lets look at the code that will go in custom/test.php (or in your case unique_book_name.php):
/* disclaimer: we are not gonna get all crazy with using PDO and parameterized queries at this point,
but be aware that there is potential for sql injection here. The auth => true will help
mitigate that somewhat, but you're never supposed to trust any input, blah blah blah. */
global $db; // load the global sugarcrm database object for your query
$book_name = urldecode($_REQUEST['book_name']); // we are gonna start with $_REQUEST to make this easier to test, but consider changing to $_POST when confirmed working as expected
$book_id = urldecode($_REQUEST['book_id']); // need to make sure this still works as expected when editing an existing record
// the $db->quote is an alias for mysql_real_escape_string() It still does not protect you completely from sql injection, but is better than not using it...
$sql = "SELECT id FROM book_module_table_name WHERE deleted = 0 AND name = '".$db->quote($book_name)."' AND id <> '".$db->quote($book_id)."'";
$res = $db->query($sql);
if ($db->getRowCount($res) > 0) {
echo 'exists';
}
else {
echo 'unique';
}
A note about using direct database queries: There are api methods you can use to accomplish this. (hint: $bean->retrieve_by_string_fields() - check out this article if you wanna go that route: http://developer.sugarcrm.com/2012/03/23/howto-using-the-bean-instead-of-sql-all-the-time/) However, I find the api to be rather slow and ajax should be as fast as possible. If a client asked me to provide this functionality there's a 99% chance I'd use a direct db query. Might use PDO and parameterized query if I'm feeling fancy that day, but it's your call.
Using the above code you should be able to navigate to https://crm.yourdomain.com/index.php?entryPoint=test and run the code we just wrote.
However at this point all you're gonna get is a white screen. If you modify the url to include the entryPoint part and it loads your home page or does NOT go to a white screen there are 3 potential causes:
You put something different for $entry_point_registry['test']. If so change the url to read index.php?entryPoint=whatever_you_put_as_the_array_key
You have sugar in a folder or something on your domain so instead of crm.yourdomain.com it is located somewhere ugly and stupid like yourdomain.com/sugarcrm/ if this is the case just make sure that your are modifying the url such that the actual domain portion is preserved. Okay I'll spell it out for you... https://yourdomain.com/sugarcrm/index.php?entryPoint=test
This is more rare, but for some reason that I cannot figure out apache sometimes needs to be reloaded when adding a new entrypoint. If you have shell access a quick /etc/init.d/apache2 reload should do the trick. If you don't have shell access you may need to open a ticket with your hosting provider (or get a fricking vps where you have some control!!!, c'mon man!)
Still not working? Did you notice the "s" in https? Try http instead and buy a fricking $9 ssl cert, geez man!
Okay moving on. Let's test out the entryPoint a bit. Add a record to the book module. Let's add the book "War of Art" (no, not Art of War, although you should give that a read too).
Now in the url add this: index.php?entryPoint=test&book_name=Art%20of%20War
Oh gawd that url encoding is hideous right! Don't worry about it.
You should hopefully get an ugly white screen with the text "exists". If you do let's make sure it also works the other way. Add a 2 to the book name in the url and hopefully it will now say "unique".
Quick note: if you're using Sugar you're probably also using mysql which is case insensitive when searching on strings. If you really need case sensitivity check out this SO article:
How can I make SQL case sensitive string comparison on MySQL?
Okay so now we have our entryPoint working and we can move on to the fun part of making everything all ajaxical. There are a couple ways to go about this, but rather than going the most basic route I'm gonna show you what I've found to be the most reliable route.
You probably will need to create the following file: custom/modules/CUSTOM_BOOK_MODULE/views/view.edit.php (I hope by now I don't need to point out changing that path to use your module name...
Assuming this file did not exist and we are starting from scratch here is what it will need to look like:
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
class CUSTOM_BOOK_MODULEViewEdit extends ViewEdit
{
public function display()
{
// make sure it works in the subpanel too
$this->useForSubpanel = true;
// make the name value available in the tpl file
$this->ss->assign('name_value', $this->bean->name);
// load the parsed contents of the tpl into this var
$name_input_code = $this->ss->fetch('custom/modules/CUSTOM_BOOK_MODULE/tpls/unique_book_checker.tpl.js');
// pass the parsed contents down into the editviewdefs
$this->ss->assign('custom_name_code', $name_input_code);
// definitely need to call the parent method
parent::display();
}
}
Things are looking good. Now we gotta write the code in this file: custom/modules/CUSTOM_BOOK_MODULE/tpls/unique_book_checker.tpl.js
First a couple of assumptions:
We're going to expect that this is Sugar 6.5+ and jquery is already available. If you're on an earlier version you'll need to manually include jquery.
We're going to put the event listener on the name field. If the book name value that you want to check is actually a different field name then simply adjust that in the javascript below.
Here is the code for custom/modules/CUSTOM_BOOK_MODULE/unique_book_checker.tpl.js:
<input type="text" name="name" id="name" maxlength="255" value="{$name_value}" />
<span id="book_unique_result"></span>
{literal}
<script type="text/javascript">
$(document).ready(function() {
$('#name').blur(function(){
$('#book_unique_result').html('<strong> checking name...</strong>');
$.post('index.php?entryPoint=test', {book_name: $('#name').val(), book_id: $('[name="record"]').val()}, function(data){
if (data == 'exists') {
removeFromValidate('EditView', 'name');
addToValidate('EditView', 'name', 'float', true, 'Book Name Must be Unique.');
$('#book_unique_result').html('<strong style="color:red;"> ✗</strong>');
}
else if (data == 'unique') {
removeFromValidate('EditView', 'name');
addToValidate('EditView', 'name', '', true, 'Name Required');
$('#book_unique_result').html('<strong style="color:green;"> ✓</strong>');
}
else {
// uh oh! maybe you have php display errors on?
}
});
});
});
</script>
{/literal}
Another Note: When the code detects that the name already exists we get a little hacky and use Sugar's built in validation stuff to prevent the record from saving. Basically, we are saying that if the name already exists then the name value MUST be a float. I figured this is pretty unlikely and will do the trick. However if you have a book named 3.14 or something like that and you try to create a duplicate this code will NOT prevent the save. It will tell you that a duplicate was found, but it will not prevent the save.
Phew! Okay last two steps and they are easy.
First, open the file: custom/modules/CUSTOM_BOOK_MODULE/metadata/editviewdefs.php.
Next, find the section that provides the metadata for the name field and add this customCode attribute so that it looks like this:
array (
'name' => 'name',
'customCode' => '{$custom_name_code}',
),
Finally, you'll need to do a quick repair and rebuild for the metadata changes to take effect. Go to Admin > Repair > Quick Repair & Rebuild.
Boom! You should be good to go!
I'm trying to inject a function into a webpage and call it, however no matter how I try it, whenever I try to call the function I get that it's undefined. I understand the browser reads function definitions on the page load and won't do so afterward, but is there a way for me to insert my functions in time. Here's a junk function I'd like to insert:
function makeAlert(word){
alert(word);
}
I've tried to inject it as is, append it to the body with <script> tags, and append it using <script src="makeAlert.js"></script> into the head with page-mod. However whatever I do, if I try to call the function on the page (using injected content script), the function is marked as undefined. Here is the basic code of what I thought would work:
in main.js
pageMod.PageMod({
include: "*",
contentScriptFile: self.data.url("makeAlert.js")
});
edit: as what I want to do seems to not be possible, my simple question is--Can I call a function I wrote from injected pagescript?
Content scripts are kept (mostly) separate from the web page for security reasons, see
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts
If you really want to do this, you can by instead doing:
unsafeWindow.makeAlert = function (word){
alert(word);
}
A couple of important things about doing things this way:
unsafeWindow is, well, UNSAFE. Never trust any data that come from unsafeWindow, ever.
it will go away some time this year ( unsure of the exact timing )
What you should probably do instead is using postMessage from the web page to the content script to pass mesages. For more on how to do this safely, please read these docs:
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/Communicating_With_Other_Scripts#Page_Scripts
I want to load single posts into an index page using Ajax for a WPML based site that I'm working on. Not sure what I'm missing but there seems to be a catch when using multiple languages and fetching the correct posts.
Here's the code I wrote -
function loadProject(reference) {
console.log(window.location.origin + '/references/' + reference);
// Displays "http://mysite.com/references/example-post/", which exists
$('#content').load(window.location.origin + '/references/' + reference, function() {
console.log('Load was performed.');
});
}
I don't reach the JS log after load() but I don't receive any error message either (e.g. "File not found").
"References" is a custom post type that I've created using the Types plugin, and has a file of it's own called single-references.php. I'm not entirely sure how CPTs work with WPML, and that's probably where the problem lies. I have two languages, Swedish and English, and the CPT only has one slug for its kind ("references").
[Solved]
Used the .get() method instead to retrieve the post and now it works.
Well, no console error, no network query :
it is not a WPML issue
did you check #content really exists ?
In chrome dev tool, go to Network, and trigger your function. You'll see what you receive.
I am trying to enable JavaScript in a Joomla template to behave differently depending on the page. In particular, I have set the Key Reference as that appears to be the most appropriate value I could find for this purpose. Unfortunately, I can't seem to access it in my code. I tried:
$this->params->get("keyref")
and a few other variations, but they simply returned a blank. How can I retrieve this value or is there a better way of writing page specific logic.
Related Articles
Joomla load script in a specific page: This would work, but seems like overkill for what I want to do here.
Each page can be given an alias. We can retrieve the alias using code from the forum:
function getCurrentAlias()
{
$menu = &JSite::getMenu();
$active = $menu->getActive();
return $active->alias;
}
We can then inject this into the Javascript:
var alias= '<?php echo getCurrentAlias(); ?>';
I'm not aware of keyref, but I would solve it by using the class suffix parameter you can set for each menu entry.see I would use a space in front of this suffix. With javascript you can then try to read this classname (suffix without the space) on each page.
getElementsByClassName("mysuffix");
for example
If this returns multiple objects, you know on which page you are. Will that help you?
How to update a site with some other site contents that is getting refreshed often (may be twice in a minute)?
What you're doing is called scraping a website. Try googling on that. Pay particular attention to the laws around it. If you're benefiting the company you're scraping, they'll probably help you; if you're not, they'll probably sue you.
$.load() is your friend. The following JQuery-function call will replace the current value of element (e.g. div) with the id "result" with the content of page "ajax/test.html".
$('#result').load('ajax/test.html');
or with an additional success handler:
$('#result').load('ajax/test.html', function() {
alert('Load was performed.');
});
if you would like to call one of these function every n seconds, use the following code:
setInterval(function() {
// wrap one of the above calls
}, <n>000);
edit:
for a cross-domain-solution, you can write a proxy page on your site which, upon call, loads the content of the 'other site' and echoes it.
Snippet available here: http://www.daniweb.com/code/snippet216729.html