How to get media url for each domain? - shopware

I am writing a plugin that works on the command line interface (CLI) and it pulls the URLs of images according to the APP_URL. The problem is that I need to set the media repository for each domain. The SalesChannelDomainId is defined on the $context parameter. On the frontend, the images are shown according to the domain name.
However, if I automatically add the SalesChannelDomain URL, it will cause a problem for CDN users. Should I write service for this?
$imageLinks = [];
$criteria = new Criteria($mediaIds);
$criteria->addAssociation('translations');
$medias = $this->mediaRepository->search($criteria, $context->getContext());
foreach ($medias as $media) {
$imageLinks[$media->getId()] = $media->getUrl();
}
$context has SalesChannelDomainId

Not sure if I understood your issue correctly but you may inject Shopware\Core\Content\Media\Pathname\UrlGeneratorInterface into your service and call getRelativeMediaUrl to get the relative path to the file. Then you can prepend the sales channel domains as you need them.
$relativePath = $this->urlGenerator->getRelativeMediaUrl($media);
$absolutePath = $salesChannel->getDomains()->first()->getUrl() . $relativePath;

Related

Hosting project in IIS appends project name to the URL and breaks my routing

I have a web api 2 project, and in my code I do some routing stuff myself.
I have all my actions going to a single route, so hitting localhost/<anything> will always go to one route.
In that route I am doing some custom pattern matching.
If the user goes to /Kittens/AdoptAKitten/12345
It will match against a template I have using regex, defined as /Kittens/AdoptAKitten/{something}
The problem is when I host my project locally, it ends up at localhost/KITTENCORP.ADOPTION/ which is the name of my project. As a result the route matching doesn't work.
I am not sure how to take into account this 'root' address. Previously I was just looking at the domain part of the Uri object but I need to include this part in the comparison to make it work (or disregard/remove it).
This code will however also be deployed to a server somewhere at which point it will probably be hosted on adoptionservice.kittens.org and thus adoptionservice.kittens.org/Kittens/AdoptAKitten/12345 will be the url. So it has to account for both situations.
Any ideas how I can resolve this problem?
For anyone stumbling across this and wondering the same thing, I fixed it with this code:
// When hosted in IIS it may get a virtual path such as localhost/KittenLibrary that needs including in comparisons
if (!string.IsNullOrEmpty(HttpRuntime.AppDomainAppVirtualPath))
{
urlStart = UrlCombine(urlStart, HttpRuntime.AppDomainAppVirtualPath);
}
UrlCombine is a function I pinched from another SO question:
private static string UrlCombine(string url1, string url2)
{
if (url1.Length == 0)
{
return url2;
}
if (url2.Length == 0)
{
return url1;
}
url1 = url1.TrimEnd('/', '\\');
url2 = url2.TrimStart('/', '\\');
return string.Format("{0}/{1}", url1, url2);
}

Way to access ModX database by including ModX core file?

I have a PHP file (that I cant make a snippet out of). I need to connect it to the database and rather then doing it the normal way was wondering if I could just include a header file of some sort that already has a DB connection.
Does anyone know of such a file in the Modx file structure?
Easy...
if you open the main index.php, you can see some hint:
/* define this as true in another entry file, then include this file to simply access the API
* without executing the MODX request handler */
if (!defined('MODX_API_MODE')) {
define('MODX_API_MODE', false);
}
// ...
/* execute the request handler */
if (!MODX_API_MODE) {
$modx->handleRequest();
}
That said, if you have a raw PHP file, let's say as an example: hello.php
<?php
define('MODX_API_MODE', true); // IMPORTANT!!!
require 'index.php'; // or give directory path, according to your need
// let's test it
$startPage = $modx->getObject('modResource', $modx->getOption('site_start', null, 1));
if (!$startPage) {
die('CRAP!');
}
$startPageArray = $startPage->toArray();
echo '<pre>';
print_r($startPageArray);
echo '</pre>';
die('WOOHOO');
And no, you don't have to define $modx again.
It's using the same object in index.php.
you can run any operations against your modx database if you load the modx module in your external script [in fact you can use any modx functions]
https://rtfm.modx.com/revolution/2.x/developing-in-modx/other-development-resources/loading-modx-externally
once you instantiate the modx object it will handle all your database connection details. This will work in any page, not just manager pages.
The only place which allows database connections using system database config are manager pages, yet that would require more work to write a plugin to access its classes and functions.
If you want to be able to use MODX functionality to establish connection, I suggest using it's xPDO to perform queries, for security reasons at least.
Such setup would be:
define('MODX_CORE_PATH', '/path/to/revo/core/');
define('MODX_CONFIG_KEY','config');
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
$host = 'localhost';
$username = 'your_username';
$password = 'your_password';
$dbname = 'your_database';
$port = 3306;
$charset = 'utf-8';
$dsn = "mysql:host=$host;dbname=$dbname;port=$port;charset=$charset";
$xpdo = new xPDO($dsn, $username, $password);

Dart redstone web application

Let's say I configure redstone as follows
#app.Route("/raw/user/:id", methods: const [app.GET])
getRawUser(int id) => json_about_user_id;
When I run the server and go to /raw/user/10 I get raw json data in a form of a string.
Now I would like to be able to go to, say, /user/10 and get a nice representation of this json I get from /raw/user/10.
Solutions that come to my mind are as follows:
First
create web/user/user.html and web/user/user.dart, configure the latter to run when index.html is accessed
in user.dart monitor query parameters (user.dart?id=10), make appropriate requests and present everything in user.html, i.e.
var uri = Uri.parse( window.location.href );
String id = uri.queryParameters['id'];
new HttpRequest().getString(new Uri.http(SERVER,'/raw/user/${id}').toString() ).then( presentation )
A downside of this solution is that I do not achieve /user/10-like urls at all.
Another way is to additionally configure redstone as follows:
#app.Route("/user/:id", methods: const [app.GET])
getUser(int id) => app.redirect('/person/index.html?id=${id}');
in this case at least urls like "/user/10" are allowed, but this simply does not work.
How would I do that correctly? Example of a web app on redstone's git is, to my mind, cryptic and involved.
I am not sure whether this have to be explained with connection to redstone or dart only, but I cannot find anything related.
I guess you are trying to generate html files in the server with a template engine. Redstone was designed to mainly build services, so it doesn't have a built-in template engine, but you can use any engine available on pub, such as mustache. Although, if you use Polymer, AngularDart or other frameowrk which implements a client-side template system, you don't need to generate html files in the server.
Moreover, if you want to reuse other services, you can just call them directly, for example:
#app.Route("/raw/user/:id")
getRawUser(int id) => json_about_user_id;
#app.Route("/user/:id")
getUser(int id) {
var json = getRawUser();
...
}
Redstone v0.6 (still in alpha) also includes a new foward() function, which you can use to dispatch a request internally, although, the response is received as a shelf.Response object, so you have to read it:
#app.Route("/user/:id")
getUser(int id) async {
var resp = await chain.forward("/raw/user/$id");
var json = await resp.readAsString();
...
}
Edit:
To serve static files, like html files and dart scripts which are executed in the browser, you can use the shelf_static middleware. See here for a complete Redstone + Polymer example (shelf_static is configured in the bin/server.dart file).

How to get local file system path in azure websites

I have a file hosted on the disk along with my website that I want to read .Not sure how do I access the file when I use System.Environment.CurrentDirectory it point to a D drive location .Can someone please tell me how can I get to my file stored at the root of where my site is hosted.
Thanks
There is an environment variable called HOME in your website's environment that will get you part way there.
You can access it using razor syntax or in code (C#). For example, suppose you have a file called data.txt that is at the root of your site with the default document and the rest of your files. You could get it's full path like this.
#{ var dataFileName = Environment.GetEnvironmentVariable("HOME").ToString() + "\\site\\wwwroot\\data.txt"; }
You can find this out on your own using the Site Control Management/"Kudu". For example, if your website is contoso.azurewebsites.net, then simply navigate to contoso.scm.azurewebsites.net. In here you can learn all about the file system and environment variables available to your website.
For testability, I use below code.
string path = "";
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HOME")))
path = Environment.GetEnvironmentVariable("HOME") + "\\site\\wwwroot\\bin";
else
path = ".";
path += "\\Resources\\myfile.json";
In above example, I added myfile.json file to Resources folder in a project with Content and Copy if newer property setting.
This works for me in both localhost and azure:
Path.Combine(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath, "file_at_root.txt");
System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath is the full local path to your site's root.
I'm currently using AppDomain.CurrentDomain.BaseDirectory (.NET Core project). It returns "D:\home\site\wwwroot\" in Azure and the application root in local so the only difference is adding "bin\\" when it is Azure. I am searching the entire directory tree, just in case, but it can be trimmed.
It's something like:
private static string GetDriverPath(ILogger logger, string fileName)
{
var path = AppDomain.CurrentDomain.BaseDirectory;
if (File.Exists(Path.Combine(path, fileName)))
{
return path;
}
string[] paths= Directory.GetFiles(path, fileName, SearchOption.AllDirectories);
if (paths.Any())
{
return Path.GetDirectoryName(paths.First());
}
throw new FileNotFoundException($"{fileName} was not found in {path}.", fileName);
}
I'm new answering questions and this is an old one but I hope it helps someone.
You can do it using the below code.
string fullFilePath = Environment.GetEnvironmentVariable("HOME") != null
? Environment.GetEnvironmentVariable("HOME") + #"\site\wwwroot\test.txt" //It will give the file directory path post azure deployment
: Path.GetDirectoryName(Path.GetDirectoryName(Directory.GetCurrentDirectory())) + #"\test.txt";//It will give the file directory path in dev environment.

Standalone PHP script using Expression Engine

Is there a way to make a script where I can do stuff like $this->EE->db (i.e. using Expression Engine's classes, for example to access the database), but that can be run in the command line?
I tried searching for it, but the docs don't seem to contain this information (please correct me if I'm wrong). I'm using EE 2.4 (the link above should point to 2.4 docs).
The following article seems to have a possible approach: Bootstrapping EE for CLI Access
Duplicate your index.php file and name it cli.php.
Move the index.php file outside your DOCUMENT_ROOT. Now, technically, this isn’t required, but there’s no reason for prying
eyes to see your hard work so why not protect it.
Inside cli.php update the $system_path on line 26 to point to your system folder.
Inside cli.php update the $routing['controller'] on line 96 to be cli.
Inside cli.php update the APPPATH on line 96 to be $system_path.'cli/'.
Duplicate the system/expressionengine directory and name it system/cli.
Duplicate the cli/controllers/ee.php file and name it cli/controllers/cli.php.
Finally, update the class name in cli/controllers/cli.php to be Cli and remove the methods.
By default EE calls the index method, so add in an index method to do what you need.
#Zenbuman This was useful as a starting point although I would add I had issues with all of my requests going to cli -> index, whereas I wanted some that went to cli->task1, cli->task2 etc
I had to update *system\codeigniter\system\core\URI.php*so that it knew how to extract the parameters I was passing via the command line, I got the code below from a more recent version of Codeigniter which supports the CLI
// Is the request coming from the command line?
if (php_sapi_name() == 'cli' or defined('STDIN'))
{
$this->_set_uri_string($this->_parse_cli_args());
return;
}
// Let's try the REQUEST_URI first, this will work in most situations
and also created the function in the same file
private function _parse_cli_args()
{
$args = array_slice($_SERVER['argv'], 1);
return $args ? '/' . implode('/', $args) : '';
}
Also had to comment out the following in my cli.php file as all routing was going to the index method in my cli controller and ignoring my parameters
/*
* ~ line 109 - 111 /cli.php
* ---------------------------------------------------------------
* Disable all routing, send everything to the frontend
* ---------------------------------------------------------------
*/
$routing['directory'] = '';
$routing['controller'] = 'cli';
//$routing['function'] = '';
Even leaving
$routing['function'] = '';
Will force requests to go to index controller
In the end I felt this was a bit hacky but I really need to use the EE API library in my case. Otherwise I would have just created a separate application with Codeigniter to handle my CLI needs, hope the above helps others.
I found #Zenbuman's answer after solving my own variation of this problem. My example allows you to keep the cron script inside a module, so if you need your module to have a cron feature it all stays neatly packaged together. Here's a detailed guide on my blog.

Resources