How to use the same path prefix for resources (css and images) for development and production environment? - sapui5

I am linking to resources such as css or image files in the application made with UI5 but I am facing a problem.
Usually, the path are for example:
return "css/style1.css"; // for a stylesheet inside the folder css
return "img/image.jpg"; // for a image inside the folder img
And once the application deployed it works fine but when running with SAP Web IDE both are not found resulting in the 404-error.
So for running in SAP Web IDE, showing the style and image the path is:
return "../../../../../webapp/css/style1.css"; #for a stylesheet inside folder css
return "../../../../../webapp/img/image.jpg"; #for a image inside folder img
And that is working running with SAP Web IDE but it's not clean. And I'm not sure either if it can work once it's deployed.
Is it a possible configuration or trick to be able to use standard link and run the app with SAP Web IDE for seing the result before to deploy?

There is a solution to create a model for this:
In model.js:
createImageRoot: function() {
var oModel = new JSONModel({
path: sap.ui.require.toUrl("the.namespace") // available since 1.58. Otherwise, use jQuery.sap.getModulePath instead.
});
oModel.setDefaultBindingMode("OneWay");
return oModel;
},
In Component.js, add in the init method
this.setModel(models.createImageRoot(), "imageRoot");
And then for example :
return this.getView().getModel("imageRoot").getProperty("/path") + "img/image.jpg"

Related

Flutter web can't load network image from another domain

I can't load network images in flutter web from other domains with API calls. getting this error
Trying to load an image from another domain? Find answers at:
https://flutter.dev/docs/development/platform-integration/web-images
ImageCodecException: Failed to load network image.
any help?
For being able to display your images from any other Domain or from Firebase Storage on a Flutter web page you have to configure your data for CORS.
Open the GCP console, select your project and start a cloud terminal session by clicking the >_ icon button in the top navbar.
Click the open editor button (pencil icon), then create the cors.json file.
The cors.json file should look like this:
[
{
"origin": ["*"],
"method": ["GET"],
"maxAgeSeconds": 3600
}
]
I set the origin to * which means that every website can display your images. But you can also insert the domain of your website there to restrict access.
Run gsutil cors set cors.json gs://your-bucket
If you need more information: https://cloud.google.com/storage/docs/configuring-cors
There are two ways to resolve this either run your app using HTML renderer or set up the CORS configuration.
1. Using HTML renderer
Taken from the docs
CORS is a mechanism that browsers use to control how one site accesses the resources of another site. It is designed such that, by default, one web-site is not allowed to make HTTP requests to another site using XHR or fetch. This prevents scripts on another site from acting on behalf of the user and from gaining access to another site’s resources without permission
When using <img>, <picture>, or <canvas>, the browser automatically blocks access to pixels when it knows that an image is coming from another site and the CORS policy disallows access to data.
Flutter has two renderers for web, canvaskit and html
When you run/build app on the flutter web it uses renderers based on the device where its running.
HTML renderer: when the app is running in a mobile browser.
CanvasKit renderer: when the app is running in a desktop browser.
auto (default) - automatically chooses which renderer to use.
The HTML renderer can load cross-origin images without extra configuration.
so you could use these commands to run and build the app.
flutter run -d chrome --web-renderer html // to run the app
flutter build web --web-renderer html --release // to generate a production build
source: https://docs.flutter.dev/development/tools/web-renderers
2. Setup CORS Configuration
Download the Google-cloud-sdk which contains a tool called gsutil
In your flutter project create a file called cors.json and add this json file which will remove all domain restrictions.
[
{
"origin": ["*"],
"method": ["GET"],
"maxAgeSeconds": 3600
}
]
Run gcloud init (located in google-cloud-sdk/bin)
Authenticate yourself by clicking the link and choose the project in the console.
finally execute gsutil cors set cors.json gs://<your-bucket-name>.appspot.com You can find your bucket name in firebase storage.
I have documented this entire process in github gists
i solve this issue by using html renderer
flutter build web --release --web-renderer html
or
flutter run --web-renderer html
for me flutter run -d chrome --web-renderer html worked.
Simply.. in your flutter (web/index.html) add this:
If you use firebase storage just follow these steps:
Open Google Cloud Console at your project
Click on console icon in top right corner
Click Open editor
Click File->New->cors.json
Place code below
[
{
"origin": ["*"],
"method": ["GET"],
"maxAgeSeconds": 3600
}
]
Then Run in console
**
gsutil cors set cors.json gs://bucket-name
**
bucket-name is the name of the storage bucket which you can find on your firebase project above the folders in the storage section
Add this code in your web/index.html file
<script type="text/javascript">
window.flutterWebRenderer = "html";
</script>
To debug quickly, instead of flutter run -d chrome --web-renderer html running from the terminal you can also add the arguments --web-renderer html on your run config. On the menu bar, navigate through Run > Edit Configurations
If you can't update CORS settings or add proxy, prefer CanvasKit (has better performance) over HTML renderer - could display image with platform view:
import 'dart:html';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class MyImage extends StatelessWidget {
#override
Widget build(BuildContext context) {
String imageUrl = "image_url";
// https://github.com/flutter/flutter/issues/41563
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
imageUrl,
(int _) => ImageElement()..src = imageUrl,
);
return HtmlElementView(
viewType: imageUrl,
);
}
}
just add cors.json in web folder
[
{
"origin": ["*"],
"method": ["GET"],
"maxAgeSeconds": 3600
}
]
After creating cors.json and running the commands, nothing worked!
Finally what worked was adding a script into index.html inside your web folder.
<script type="text/javascript">window.flutterWebRenderer = "html";</script>
Reference Image:
it seems it solved my issue by running flutter run --web-renderer html
This official solution worked for me on Chrome only (Source). But I had to run it first every time.
flutter run -d chrome --web-renderer html
And disabling web security also worked (Source). But the browsers will show a warning banner.
But In case you are running on a different browser than Chrome (e.g. Edge) and you want to keep 'web security' enabled.
You can change the default web renderer in settings in VS Code
File ==> Preferences ==> Settings ==> Enter 'Flutter Web' in the Search Bar ==> Set the default web renderer to html
For someone who uses Slim Framework, just create a .htaccess file on that folder for storing images and put this line of code
Header set Access-Control-Allow-Origin *
There is a two way to solve this issue :
1- Just run your flutter web with flutter run -d chrome --web-renderer html
But there is one problem when you renderer your canvas view to HTML view. So your all views like images, text, etc gone blurry(bad quality). If you sacrifice the quality so go on with the first solution.
If you don't sacrifice quality so you need to add some code to your backend site I have done with node js you can use with yours.
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
So, I chose the second solution because I don't sacrifice quality. So I provide you with my backend screenshot for better understanding hope you found your solution.
I had an issue while loading content from other domains which I don't have control over to change the cors settings from the server-side. I have found a work around for this problem.
STEP 1: go to C:\src\flutter\packages\flutter_tools\lib\src\web or navigate from your flutter root directory to flutter\packages\flutter_tools\lib\src\web.
STEP 2: open chrome.dart in your text editor.
STEP 3: add '--disable-web-security' under the like '--disable-extensions' as save the file.
STEP 4: run flutter clean in your project folder and run the app.
Hope this works for you.
I haven't tested my application in production yet. Adding this flag might also cause some security issues.
The Ultimate Solution
Use this package instead of flutter's NetworkImage.
https://pub.dev/packages/image_network
Tested on Flutter web with Canvas renderer and it works like a charm!
Verified answer is correct, Upvoted you man. Here's how I did it

How to transform Flutter web app into a PWA?

I am making a new Web application with Flutter web.
I want to add service workers to my app in order to make it a PWA.
What I need to use to achieve my goal ?
I have tried to do it with dart packages (service_worker or pwa), but they are deprecated for Dart 2.
Since a service worker is just javascript code you can write one in plain javascript (e.g. in a sw.js file), bundle it with your flutter web app and then register the service worker from the index.html file like this (source):
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
}
You currently need to switch to Flutter Channel Dev (at Feb 17th 2020, current Dev version 1.15.3)
For existing projects, use the terminal to hit the command:
flutter create .
For new projects, you would have a 2 files: manifest.json and index.html inside your web folder.
While checking index.html you would notice the following script:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/flutter_service_worker.js');
});
}
If the script is not there, added inside <body></body>
This script creates the PWA support for you and be able to "install" the app on a device (this also includes MacOS)
Follow this GUIDE to complete your PWA functionalities.

Fiori launchpad fails to load UI5 app because it cannot find registered custom module

I've a custom control in my app. It runs as a standalone web application fine. But when I'm launching it from the Fiori Launchpad (FLP), it logs an error in the console. I've registered my control in my index.html file:
<script>
sap.ui.localResources("sap.custom");
sap.ui.localResources("sap.ui.codetools");
sap.ui.localResources("libs");
</script>
The structure of file directory is:
If I remove my custom control, then I can run my app in the launchpad. Do I need to add some settings in my manifest file? What could be the reason for this error?
The common configuration for a Fiori tile is to point to the Component file. So the index.html file is not deployed together with the app. If it's absolutely necessary to register additional module paths, do it in manifest.json instead.
{
"sap.ui5": {
"resourceRoots": {
"sap.ui.codetools": "sap/ui/codetools"
}
}
}
Documentation: Descriptor for Applications, Components, and Libraries (See resourceRoots).
The common configuration for a Fiori tile is to point to the Compopent.js file. So the index.html is not called. The FioriLaunchpad.html plays the role of the index.html and the ComponentContainer is defined there.
Try to register your custom control somewhere else within the app.
Activate the APP Node on SICF( node Name is same as App)

How to setup a project to run using JNLP?

I currently have an application that runs from the desktop, but I would like to make it deployable from web browsers using JNLP. How do you go about setting up jnlp for an application? Are there any examples that go more in depth then a hello world?
So I ended up following this reference http://www.oracle.com/technetwork/articles/javase/jnlp-142088.html to build my jnlp. On a side note to add more jars etc all you need to do is list more jar tags and tag one as main. Use this to reference the structure and required tages https://docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/jnlpFileSyntax.html#extension

How to enable TinyMCE plugins in Orchard TinyMCEDeluxe

I've followed all the guidelines found at http://tinymcedeluxe.codeplex.com/
I've created the following ResourceManifest.cs file under my theme folder:
using Orchard.UI.Resources;
namespace TinyMceDeluxe {
public class ResourceManifest : IResourceManifestProvider {
public void BuildManifests(ResourceManifestBuilder builder) {
var manifest = builder.Add();
manifest.DefineScript("OrchardTinyMce")
.SetUrl("orchard-tinymce.js")
.SetDependencies("TinyMce")
.SetVersion("3.4.8");
}
}
}
I've added the following line to my Layout.cshtml file:
Script.Require("OrchardTinyMce").AtFoot();
And I've copied the orcard.tinymce.js file to my theme's script folder.
The correct files are loaded, as I can check using Firebug. The problem is that as soon as I uncomment any of the plugins to use, the TinyMCE editor commands simply disappear. For example:
////
//// Un-comment-out the tinymce.PluginManager.load commands for the plugins you want to use:
tinymce.PluginManager.load('advhr', '/Modules/TinyMceDeluxe/Scripts/plugins/advhr/editor_plugin.js');
//tinymce.PluginManager.load('advimage', '/Modules/TinyMceDeluxe/Scripts/plugins/advimage/editor_plugin.js');
//tinymce.PluginManager.load('advlink', '/Modules/TinyMceDeluxe/Scripts/plugins/advlink/editor_plugin.js');
//tinymce.PluginManager.load('advlist', '/Modules/TinyMceDeluxe/Scripts/plugins/advlist/editor_plugin.js');
...
If I uncomment the advhr plugin the TinyMCE editor does not show. If I comment it back again it shows perfectly. NOT A SINGLE PLUGIN WORKS. As soon as I uncomment one of them, even if I do not use it on the TinyMCE.init call, it doesn't work. Even using the following init which is the standard one:
tinyMCE.init({
theme: "advanced",
mode: "specific_textareas",
editor_selector: "tinymce",
plugins: "fullscreen,autoresize,searchreplace,mediapicker",
theme_advanced_toolbar_location: "top",
theme_advanced_toolbar_align: "left",
theme_advanced_buttons1: "search,replace,|,cut,copy,paste,|,undo,redo,|,mediapicker,|,link,unlink,charmap,emoticon,codeblock,|,bold,italic,|,numlist,bullist,formatselect,|,code,fullscreen",
theme_advanced_buttons2: "",
theme_advanced_buttons3: "",
convert_urls: false,
valid_elements: "*[*]",
// shouldn't be needed due to the valid_elements setting, but TinyMCE would strip script.src without it.
extended_valid_elements: "script[type|defer|src|language]"
});
I don't know if I did anything wrong. I've downloaded the package and unzipped it, then I've copied the TinyMceDeluxe folder to the Orchard.Web\Modules folder. It now sits just bellow the standard TinyMce plugin folder. Then I've enabled it on the Modules admin UI. And created the ResourceManifest and copied and configured the orchard-tinymce.js file on my theme's folder.
In desperation I've even deleted the ResourceManifest.cs file and the orchard-tinymce.js file from my theme's folder and used everything under the TinyMceDeluxe module itself, but it still didn't work.
Does anyone have any ideas why is this happening, and why it is so hard to use some basic tinyMce plugins in Orchard? I've never had trouble with it in Wordpress :-(. Can I add everything by hand without using the modules? I would add it to my theme and try it out...
EDIT:
Giscard correctly answered that the culprit of the problem was the actual root path for my Orchard application. The loading of the plugins try to read them from the root, but when running the application inside Visual Studio Orchard normally runs it as if the root path was OrchardLocal. This creates a lot of problem, but they are necessary problems because if it didn't run with a different root you would only notice the need to be aware of different roots when you deployed your site on a different environment, with a different root. So I had to add the root to all plugin loading like this:
//tinymce.PluginManager.load('advhr', '/modules/tinymcedeluxe/scripts/plugins/advhr/editor_plugin.js');
tinymce.PluginManager.load('advhr', '/orchardlocal/modules/tinymcedeluxe/scripts/plugins/advhr/editor_plugin.js');
Unfortunately adding this root prefix in a hardcoded fashion will not cut it because when deploying to a different server it will fail as the root will probably change.
I had to resort to a dirty JavaScript trick to resolve it. I've put the following JavaScript code inside the orchard-tinymce.js file:
// Getting the root of the site by inspecting the script tag that just loaded this script...
var scripts = document.getElementsByTagName("script");
var scriptTag = scripts[scripts.length - 1];
var scriptPath = scriptTag.getAttribute("src");
var idx = scriptPath.indexOf("/Modules");
var appPath = scriptPath.substring(0, idx);
Now appPath will hold the path for the root of our application.
Then all I had to do was to change the plugin loading code to this:
tinymce.PluginManager.load('table', appPath + '/modules/tinymcedeluxe/scripts/plugins/table/editor_plugin.js');
And it is working and hopefully will work with any root or even if the site is actually at the root (the appPath will return an empty string).
TinyMceDeluxe author here. By coincidence I just published a new version of the module a few minutes ago. I recommend you try out the new one (v1.0.1), as getting it working is easier than previous versions. It is now a replacement rather than an add-on to the default TinyMce module, so you simply disable TinyMce, and enable TinyMceDeluxe.
Starting with v1.0.1, you no longer need to touch resourcemanifest.cs, or override any views/scripts/etc.
TinyMceDeluxe goes into the Modules folder, as a sibling to TinyMce. Your modules folder will look like this:
modules/
TinyMce/
TinyMceDeluxe/
Then you customize /modules/tinymcedeluxe/scripts/orchard-tinymce.js. The one that's there by default has bunch of info on what you can change.