I'm integrating CKEditor into a CakePHP app which is running on a Zeus server (and therefore can't use .htaccess - I have to use rewrite.script instead). Problem is, the paths that CKEditor puts into the head section of the page don't work, so the editor won't load.
For instance, one generated path is:
http://www.example.com/js/ckeditor/config.js?t=B8DJ5M3
If I go to
http://www.example.com/js/ckeditor/config.js
I can see the file, but as soon as I add the ?t=B8DJ5M3 on the end, Cake complains that it can't find the jsController.
I'm not sure what to do about this - whether to dig around in CakePHP, CKEditor or the rewrite.script files! What should I try next?
That query string on the end of your URL is used to make sure the file isn't cached. Seems like something in your GET request configuration/routing on the Zeus server is trying to locate that exact file including the query string. You're going to need to create a Rewrite that performs a goto on the URL minus the query string. I found a pretty solid article in the Drupal forums where someone put together a script that may help you: http://drupal.org/node/46508
RULE_0_START:
# get the document root
map path into SCRATCH:DOCROOT from /
# initialize our variables
set SCRATCH:ORIG_URL = %{URL}
set SCRATCH:REQUEST_URI = %{URL}
# see if theres any queries in our URL
match URL into $ with ^(.*)\?(.*)$
if matched then
set SCRATCH:REQUEST_URI = $1
set SCRATCH:QUERY_STRING = $2
endif
RULE_0_END:
And from there, handle your goto minus the query string. Hope that helps
You can do this
In the view where you want to display the editor, put the following script on the top of the page (or somewhere before textarea which you want to contain editor):
<?php echo $this->Html->script('ckeditor/ckeditor');?>
This scipt will include the "webroot/js/ckeditor.js" file to your view.
Create the textarea and give it a class named "ckeditor"
<?php echo $this->Form->textarea('content',array('class'=>'ckeditor'))?>
Voila! The editor is now displaying instead of raw textarea.
Related
This snippet from the Laravel docs, https://laravel.com/docs/5.5/responses#file-responses, seems to be just what I need:
return response()->file($pathToFile);
The problem is that my file is stored on an S3 disk, and I can't seem to reference it properly.
I tried using Storage::disk('s3')->getDriver()->getAdapter()->applyPathPrefix($myPath); to get the fully qualified file name. It just returned the value of $myPath though.
I then tried to get the url using Storage::disk('s3')->url($myPath); The URL looks fine however Symphony says it does not exist. When I check with Storage::disk('s3')->exists($myPath); however, it returns true.
How do I go about displaying a file from cloud storage directly in the user's browser?
EDIT:
More details below:
To save the item in the first instance I am using $map->storeAs('/my/path/maps/filename.pdf', ['disk' => 's3']);
The output of url() is "https://s3.ap-southeast-2.amazonaws.com/my.domain.com/my/path/maps/filename.pdf"
When I cut-and-past the url in a browser address bar, it loads no problem
It seems to me that the response()->file() method does not accept a URL parameter. Is that the case?
Question - does the file need to be publicly available? (It currently is, but I would prefer to make it private).
The following should be enough:
$path = Storage::disk('s3')->url($filename);
I don't have a working example set up, but from memory $filename should be the same as what you originally placed in the ->put() method. So when you say $mypath in your question, hopefully that isn't already prefixing stuff like your s3 instance.
If this isn't the case, can you edit the question to include the result of ->url() and an example of your put() call and $path.
Looking at the edit, I think I understand what you are trying to do, which has been solved here:
https://laracasts.com/discuss/channels/laravel/file-response-from-s3
A client wants to move a website they made using Adobe Catalyst to a different hosting provider. I was able to copy the entire website via FTP and move it to the new host. Everything looks fine except for many of the links leaving code that looks like this:
{module_contentholder, name="_U309"} {module_contentholder, name="_U299"}
Does anyone know what this is or how to fix it?
Those are references to Content Holders. They work similarly to PHP's include statement, but the file they reference is fixed to a single path: /_System/ContentHolders/.
You will likely come across more tags like that, such as {module_menu} and {tag_pagecontent}. You'll need to manually adapt them to the whatever the new host uses. The documentation will help: http://docs.businesscatalyst.com/reference/
The obtuse names of the content holders shown in your example indicates the site was likely to have been generated by Adobe Muse, a WYSIWYG editor. I strongly recommend that you find the original .muse project files, and use those to update the site. Muse can compile the site for platforms other than Business Catalyst.
Through research I found out there is no way to get those codes to display properly without editing each page individually. Fortunately, I wrote a PHP script that goes through the code of each page and replaces it automatically.
Step 1: Make a file in the index directory called replacement.php
Step 2: Put this code in
$file = $_GET['file'];
$path = '/path/to/public_html/' . $file;
$file_contents = file_get_contents($path);
preg_match_all("/{module(.*?)}/", $file_contents, $matches);
foreach($matches[0] as $match) {
if(preg_match('/\"([^\"]*?)\"/', $match, $query)) {
$queryNew = str_replace("\"", "", $query[0]);
$queryPath = '/path/to/public_html/_System/ContentHolders/' . strtolower($queryNew) . '.html';
$queryContents = file_get_contents($queryPath);
$file_contents = str_replace($match, $queryContents, $file_contents);
}
}
file_put_contents($path, $file_contents);
Step 3: Replace where it says /path/to/public_html/ to your domain files location.
Step 4: go to http://www.yourdomain.com/replacement.php?file=index.html to change over the index file. You can change "index.html" in the url to any other page you want converted.
Hopefully this helps someone else in the future.
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!
Anyway to examine the final XML structure magento comes up with after parsing & combining all the different XML files?
There is nothing of that sort which turned up on searching on the internet and I think for someone like me, magento layouts were a bit too much in the beginning & I would try to do everything on the code side.
Another thing which will help in picking up the name of different nodes that we can use, right away from the final XML structure.
Never ran into this but I believe we will have a better picture of what's overriding what.
The following will get you the merged configuration from app/etc/*.xml, app/etc/modules/*.xml, as well as each (active) module's config.xml file; when retrieving the config though there is no indication of what was overwritten, as the merges happen as each config file is being parsed:
Mage::getConfig()->getNode()->asNiceXml(); // or asXML() | asArray() | etc.
However, you seem to be asking about how the application makes use of this information. This is a function of application design.
Also, you mention "all of the different XML files." It's worth noting that these are not maintained in one massive object instance. For example, layout XML is accessed using the layout update object Mage_Core_Model_Layout_Update and can be accessed meaningfully after it's been loaded and manipulated for a given rendering scope (e.g. loadLayout() in a controller action):
Mage::app()->getLayout()->getUpdate()->asString(); // or asSimplexml() or asArray()
Yes - Commercebug. As well as a whole load of other useful features, you can also view the entire XML structure that Magento has produced.
http://store.pulsestorm.net/products/commerce-bug-2
I believe the following will output the XML: echo Mage::getConfig()->getXmlString();
You can create a script with something like this:
header("Content-Type:text/xml");
require_once '../app/Mage.php';
Mage::app();
echo Mage::getConfig()->getXmlString();
based on answer from benmarks I did
echo "<pre>".htmlspecialchars(Mage::getConfig()->getNode()->asNiceXml())."</pre>";
If you want for example to see the blocks configuration in Magento 1 you can put this in a file, place the file at the root of the site and navigate to it in a browser:
<?php
include("app/Mage.php");
Mage::app();
//just see blocks...
echo "<pre>".htmlspecialchars(Mage::getConfig()->getNode()->global->blocks->asNiceXml())."</pre>";
die();
I have successfully created new rules for the prototype validation, now I need to translate the error messages (Location: String in Javascript). However, I can only translate all the messages, my new custom ones don't appear to be translatable. How do I change this?
Maybe you need an jstranslator.xml file inside etc folder:
<?xml version="1.0" encoding="UTF-8"?>
<jstranslator>
<some-message-name translate="message" module="mymodule">
<message>This is the text in my own js validation</message>
</some-message-name>
</jstranslator>
With the following structure and meanings:
<jstranslator> - [one] XML root node.
<some-message-name> - [zero or more] root node child elements with a unique XML element name across all jstranslator.xml files (otherwise last in loading order based on module listing wins).
Attributes:
translate="message" - (optional) a hint that the child element(s) that is being translated is named "message", however this is hardencoded for the js translation XML files (Magento CE 1.9, search for "*/message") and this attribute does not need to be used.
module="mymodule" - (optional) name of the module, if left out the value is "core". It will be used to instantiate the data-helper later on (from that module) which then is reponsible to load the translations (e.g. from the CSV files).
<message> - [zero or one per parent] message to translate. the text value of this elements node-value is taken to be added to the javascript Translator object data.
All jstranslator.xml files of activated modules are processed.
Then put your translation line into the Something_Mymodule.csv file:
"This is the text in my own js validation", "(translated in a different language or speech)"
Then in your js scripts you can use your own translations via the Translator:
Translator.translate('This is the text in my own js validation');
Further References
Correct usage of jstranslator.xml
To translate custom javascript error messages you need also to add them to the following file:
\app\code\core\Mage\Core\Helper\Js.php
find a function _getTranslateData()
and you'll see a bunch of messages already in there.
just add your message somewhere in the array like this:
'This is my validation message' => $this->__('This is my validation message')
Don't forget a comma (,).
And then put translation in some translate file.
In the file where you use this message (I use it in opcheckout.js file) you need to wrap text in Translator.translate('This is my validation message').
I haven't figured out yet if it's important which translate file that is. You can try Mage_Core.csv .
I needed it in Mage_Checkout.csv and it works in there.
Anyway, for those who are interested in more, I noticed that these javascript messages are printed in header of every html page and some worrie that it messes with the SEO. Anyway this is printed in file
\app\design\frontend\bmled\default\template\page\html\head.phtml with the code.
<?php echo $this->helper('core/js')->getTranslatorScript() ?>
Check for more here:
I hope this helps, I just hope this works everywhere, so far I tested it on Onepage Checkout only.
You can edit app/local/ur_language/Mage_Core.csv file, adding original string in the first Column the translated one in the second. All the javascript translations are stored in this file.
What's helped me (Magento EE 1.6) -
I added new translation object:
<script>
var _data = {
'This is a required field.':
"<?php echo $this->__('This is a required field.'); ?>",
'Please make sure your passwords match.':
"<?php echo $this->__('Please make sure your passwords match');?>",
'validate telephone error':
"<?php echo $this->__('validate telephone error');?>"
};
var Translator = new Translate(_data);
</script>
if it is defined VarienForm uses it in js validation
We had exactly the same problem with one of our magento projects. We found that the function in app/design/frontend/default/default/template/page/htmlhead.phtml had been commented out:
<?php echo $this->helper('core/js')->getTranslatorScript() ?>
After putting this in, it still did not work, because translations had not been inserted into translate file. Check out those two things and it should start working.
To expand on this, you must add the translation strings to Mage/Core/Helper/Js.php.