How to replace just one exception template for own exception inside a plugin, which is extended built-in exception? :)
Exception located is in /vendor/author/pluginName/src/Exception/TestException.php
But i try replace template by create file /src/Template/PluginName/Error/test.ctp but doesn't work.
Of course, if I create file inside /src/Template/Error/test.ctp works fine.
I have many plugins and each can has own TestException class.
So, How I can use /PluginName direcotry?
Cake 3.6
The correct template path for overriding a plugin template on app level starts with Template/Plugin/, followed by the plugin name and the expected local template path, ie for a plugin named Foobar, the path for overriding its test error template would be:
src/Template/Plugin/Foobar/Error/test.ctp
Also it's important to keep in mind that error templates will by default only be looked up in plugins, if the exception is being triggered in a plugin controller request, to be specific, when the current global request object (Router::getRequest(true)) has a plugin parameter set ($request->getParam('plugin'))!
It should also be noted that individual templates that map to exception/method names, will only be used for non-HTTP exceptions (\Cake\Http\Exception\HttpException), and only when debug mode is enabled, if it's a HTTP-Exception or debug mode is disabled, then only the error400 or error500 template will be used!
See also
Cookbook > Plugins > Plugin Views > Overriding Plugin Templates from Inside Your Application
Related
I'm trying to use LestMove to be more precise
the second implementation method where it says:
Option 2:
Copy the following files into your project:
PFMoveApplication.h
PFMoveApplication.m
If your project has ARC enabled, you'll want to disable ARC on the above files. You can do so by adding -fno-objc-arc compiler flag to your PFMoveApplication.m source file. See How can I disable ARC for a single file in a project?
If your application is localized, also copy the 'MoveApplication.string' files into your project.
Link your application against Security.framework.
In your app delegate's "-[applicationWillFinishLaunching:]" method, call the PFMoveToApplicationsFolderIfNecessary function at the very top.
but I'm not able to call the method / Class, could someone help me with this issue? Thanks in advance!
In general, there are a couple of ways to set up an Objective-C class in your AppleScriptObjC project:
Add the file(s) to the project - the Objective-C class name will be
the one used in the #interface/#implementation declarations
Add an outlet property in the AppleScript class/script you are using, e.g. property someProperty : missing value
Instantiate the class programmatically:
set someProperty to current application's ClassName's alloc's init()
or
Connect stuff up with the Interface Builder:
Add an NSObject (blue cube) from the library to your project
Set the class of the object/cube to the class name of the Objective-C file(s) in the Identity Inspector
Connect the AppDelegate IB Outlet to the object/cube in the Connections Inspector
After setting up the outlet property, the Objective-C methods can be used like any other script/class:
someProperty's handler()
That LetsMove project wasn't really set up for AppleScriptObjC, but I was able to tweak it a bit to get it running. I'm not that great at writing Objective-C, but the following worked for me using a new default AppleScript project with Xcode 10 in Mojave (the original file is over 500 lines long, so I'm just highlighting the changes):
Add PFMoveApplication.h and PFMoveApplication.m files to the project (the class name is LetsMove)
Add Security.framework to Link Binary With Libraries in Build Phases
As described in the original project README, add the compiler flag -fno-objc-arc to the Objective-C file in Compile Sources of the Build Phases
-- Now to alter the Objective-C files a bit:
Move the #interface declaration to the .h file and include the redefined method signatures below in it:
The PFMoveToApplicationsFolderIfNecessary and PFMoveIsInProgress methods are redefined as instance methods:
- (void)PFMoveToApplicationsFolderIfNecessary;
- (BOOL)PFMoveIsInProgress;
Redefine the above method signatures in the .m file, and include those methods in the #implementation section - to do this, move the #end to just before the helper methods (after the PFMoveIsInProgress method)
Remove the isMainThread statement at the beginning of the PFMoveToApplicationsFolderIfNecessary method - this is not not needed (AppleScript normally runs on the main thread), and fixes another issue
There is still a little stuff in there from the original app such as NSUserDefaults, so for your own project, give it a look to see if anything else needs changing (dialog text, etc)
And finally, in the AppDelegate.applescipt file, the following was added to applicationWillFinishLaunching:
current application's LetsMove's alloc's init()'s PFMoveToApplicationsFolderIfNecessary()
I'm getting to grips with the Silverstripe framework and I've come across a strange error.
Say for example I want to create a new 'membership' page. Within mysite/code I have set up a membership.php page as follows:
class Membership extends Page {
}
class Membership_Controller extends Page_Controller {
}
Then I have created a membership.ss file within my templates/layout folder with some test output. I then do a dev build and create a page in the CMS of type 'membership'. On the front end if I click the new page form the nav bar membership I don't see the test text so it seems that the template is not being read?
Any ideas?
Thanks.
Alan.
There are several common pitfalls regarding templates:
how flushing works has changed several times in the past versions.
I will not explain the details here, as those are prossibly subject to change soon again.
However there are 2 things in the current version (3.1) that is of relevance here:
/dev/build does NOT flush at all
/dev/build?flush=1 does ONLY flush manifest and config (NO templates)
(dev build does not use the template, so there is no flushing the template performed)
this means that you have do do a ?flush=1 on a normal page, not just on dev/build
The Template file has to be named exactly like the class (I think its case sensitive)
check that the template file is not overwritten by another template file in another location. (eg if you have moduleName/templates/Foo.ss and themes/simple/templates/Foo.ss than the template of the theme will overwrite the module template
make sure the template is not empty (this causes an error in SilverStripe, at least in version 3.1)
Actions on a Controller can overwrite template ussage. here some examples:
// this will not use a template at all, it will just print "some string"
public function index() { return "some string"; }
// this will not use a template at all, it will output an empty string
public function index() { return; }
// this will use template named "Bar.ss"
public function index() { return $this->renderWith(array('Bar')); }
SilverStripe also provides a debug option to see what templates are used.
you can active it by 2 ways:
set source_file_comments in your yml config:
SSViewer:
# display template filenames as comments in the html output
source_file_comments: true
use the "URL Variable Tools": just add ?showtemplate=1 when viewing your website
when enabled, see the HTML source (CTRL+u in firefox) of the page
silverstripe will add comments to let you know what templates are used.
Make sure your class has a Page_Controller extension declared and named correctly. I recently had this issue. The page controller extension had a typo, so the template file was not being used.
So for example, if your page class is RidiculouslyNamedPage
class RidiculouslyNamedPage extends Page {
}
class RidiculouslyNamedPage_Controller extends Page_Controller {
}
Then in your themes/[theme-name]/templates/Layout/ folder you would have your RidiculouslyNamedPage.ss.
If you misspell RidiculouslyNamedPage_Controller the template will not get called.
I found the answer to the problem.
My .php was missing the following:
function getInfo() {
return $this->renderWith('Media');
}
ithout this the Media.ss file will not be used! Hopefully this will help other who might be getting to grips with SS!
for the automated generation of pdfs from the page content I want to use the renderWith function within onAfterWrite in the Page Class (later with DOMPDF the PDF will be generated from the returned HTML):
public function onAfterWrite() {
parent::onAfterWrite();
$this->renderPdf();
}
public function renderPdf() {
return $this->renderWith(array('Pdf'));
}
There is always this Error returned when saving the Page: None of these templates can be found in theme 'mytheme': Pdf.ss
The Template exists for sure and calling the Function renderPdf via a Template works perfectly. This is a bit weird. (ss 3.1.1)
many thanks,
florian
EDIT: maybe it is related to 3.1, I just tested in 3.0.5. without any issues. In a clean 3.1.2 install I was able to reproduce the error.
Where is your template located exactly?
Have you tried to put it under the 'templates' folder, and not under 'Layout' or 'Includes'?
In your case, I would try to move that file here:
/themes/mytheme/templates/Pdf.ss
As you are calling for a standalone template (so not alongside 'Page' for example), the .ss file should be accessible as a 'root' template, as opposed to a layout template.
Is it possible to use JavaLoader to get objects returned by CF-called web services, and JavaLoader-loaded objects to be the same classpath context? I mean, without a lot of difficulty?
// get a web service
ws = createObject("webservice", local.lms.wsurl);
// user created by coldfusion
user = ws.GenerateUserObject();
/* user status created by java loader.
** this api provider requires that you move the stubs
** (generated when hitting the wsdl from CF for the first time)
** to the classpath.
** this is one of the stubs/classes that gets called from that.
*/
UserStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
// set user status: classpath context clash
user.setStatus(UserStatus.Active);
Error:
Detail: Either there are no methods with the specified method name and
argument types or the setStatus method is overloaded with argument
types that ColdFusion cannot decipher reliably. ColdFusion found 0
methods that match the provided arguments. If this is a Java object
and you verified that the method exists, use the javacast function to
reduce ambiguity.
Message: The setStatus method was not found.
MethodName setStatus
Even though the call, on the surface, matches a method signature on user--setStatus(com.geolearning.geonext.webservices.Status)--the class is on a different classpath context. That's why I get the error above.
Jamie and I worked on this off-line and came up with a creative solution :)
(Apologies for the long answer, but I thought a bit of an explanation was warranted for those who find class loaders as confusing as I do. If you are not interested in the "why" aspect, feel free to jump to the end).
Issue:
The problem is definitely due to multiple class loaders/paths. Apparently CF web services use a dynamic URLClassLoader (just like the JavaLoader). That is how it can load the generated web service classes on-the-fly, even though those classes are not in the core CF "class path".
(Based on my limited understanding...) Class loaders follow a hierarchy. When multiple class loaders are involved, they must observe certain rules or they will not play well together. One of the rules is that child class loaders can only "see" objects loaded by an ancestor (parent, grandparent, etcetera). They cannot see classes loaded by a sibling.
If you examine the object created by the JavaLoader, and the other by createObject, they are indeed siblings ie both children of the CF bootstrap class loader. So the one will not recognize objects loaded by the other, which would explain why the setStatus call failed.
Given that a child can see objects loaded by a parent, the obvious solution is to change how the objects are constructed. Structure the calls so that one of the class loaders ends up as a parent of the other. Curiously that turned out to be trickier than it sounded. I could not find a way to make that happen, despite trying a number of combinations (including using the switchThreadContextClassLoader method).
Solution:
Finally I had a crazy thought: do not load any jars. Just use the web service's loader as the parentClassLoader. It already has everything it needs in its own individual "class path":
// display class path of web service class loader
dynamicLoader = webService.getClass().getClassLoader();
dynamicClassPath = dynamicLoader.getURLS();
WriteDump("CLASS PATH: "& dynamicClassPath[1].toString() );
The JavaLoader will automatically delegate calls for classes it cannot find to parentClassLoader - and bingo - everything works. No more more class loader conflict.
webService = createObject("webservice", webserviceURL, webserviceArgs);
javaLoader = createObject("component", "javaloader.JavaLoader").init(
loadPaths = [] // nothing
, parentClassLoader=webService.getClass().getClassLoader()
);
user = webService.GenerateUserObject();
userStatus = javaLoader.create("com.geolearning.geonext.webservices.Status");
user.setStatus(userStatus.Active);
WriteDump(var=user.getStatus(), label="SUCCESS: user.getStatus()");
I would like to load certain Twig templates from the database in my Symfony2 application, while still keeping the possibility to use the native loader to render templates in the standard filesystem locations. How to achieve this?
As far as I've understood, there is no possibility to register multiple loaders to Twig environment. I have been thinking two ways (for now):
Replace the default loader with a custom proxy class. When templates are referred with the standard #Bundle-notation, proxy would pass the request to the default loader. In other case, the request would be passed to my custom (database) loader; OR
Build up a completely new Twig environment. This method would require registering custom Twig extensions to both environments and it does not allow cross-referencing templates from different sources (some from #Bundles, some from database)
Update 1:
It seems that Twig supports Twig_Loader_Chain class that could be used in my first option. Still, the default loader should be accessible and passed to the chain as the first option.
To use Twig_Loader_Chain you need Symfony 2.2
https://github.com/symfony/symfony/pull/6131
Then you can simply configurate your loaders:
services:
twig.loader.filesystem:
class: %twig.loader.filesystem.class%
arguments:
- #templating.locator
- #templating.name_parser
tags:
- { name: twig.loader }
twig.loader.string:
class: %twig.loader.string.class%
tags:
- { name: twig.loader }
Update:
It looks like there are still some problems(the filesystem loader couldn't find the templates somtimes) but I found this:
http://forum.symfony-project.org/viewtopic.php?t=40382&p=131254
Seems to be working great!
This is a cheap and cheerful way to temporarily change the loader in your standard env:
// Keep the current loader
$oldLoader = $env->getLoader();
// Temporarily set a string loader
$env->setLoader(new \Twig_Loader_String());
// Resolve the template - but don't render it yet.
$template = $env->resolveTemplate($template);
// Restore the old loader
$env->setLoader($oldLoader);
// Render the template. This will pass the old loader into any subtemplates and make sure your string based template gets into the cache.
$result = $template->render($context);
I suppose this will work with other custom loaders.