How to create a folder with % character on SharePoint 2019 using rest calls via postman? - rest

I want to create a folder with % character on SharePoint 2019 and I am using the below call:
POST http://<site>/_api/web/folders
{
"__metadata": {
"type": "SP.Folder"
},
"ServerRelativeUrl": "/SP 2019/Folder%"
}
But this is creating a Folder%25 instead of Folder%.
If I change the character in the JSON to #, it creates a folder with '#' character.

This does the job.
POST http://<site>/_api/web/folders/AddUsingPath(decodedurl='Path')
It will create a folder with % character. It was introduced for SP Online but also works with SP 2019.
The issue i am running into is the api returns 400 Bad Request in two cases:
a) The folder already exists.
b) The URL is malformed.
I need to distinguish between the two.
You may take a look at this Microsoft's page for further refrence.

The value we passed in was ‘%’, but it seems that the server escaped it.
As a workaround ,you could create a event receiver(item added) . When a folder is created, if its name contains ‘%25’, rename it.
Sample code:
public override void ItemAdded(SPItemEventProperties properties)
{
SPFolder folder = properties.ListItem.Folder;
string name= folder.Name;
if (name.Contains("%25")) {
string newName=name.Replace("%25", "%");
folder.Item["Name"] = newName;
folder.ParentWeb.AllowUnsafeUpdates = true;
folder.Item.Update();
folder.ParentWeb.AllowUnsafeUpdates = false;
}
base.ItemAdded(properties);
}
Best regards,
Amos

Related

local rest api controller does not receive data from repository function-call

Our VS-2022 development project is Blazor WASM Core-6 with local REST-API for data. Using Postman, my testing is not getting data from the controller call to a repository function -- using breakpoints and local debugging -- as one would expect.
The repository function return statement is return Ok(vehicleTrips);. The IEnumerable vehicleTrips data variable contains the correct four records as expected from the DB fetch.
From the controller the call to the repository function is:
var result = (await motripRepository.GetMOTripsByDateRange((int)eModelType.Vehicle, pVehicleList, pDateFrom, pDateTo)!)!;
The controller function signature is:
[HttpGet("byDateRange/{pVehicleList}/{pDateFrom}/{pDateTo}")]
[ActionName(nameof(GetVehicleMOTripsByDateRange))]
public async Task<ActionResult<IEnumerable<MOTRIP>>> GetVehicleMOTripsByDateRange([FromRoute] string pVehicleList, [FromRoute] string pDateFrom, [FromRoute] string pDateTo) {
This is my problem. The result return value from the repository has a return.Value of null -- NOT four trip records as we should.
Additionally, the VS-Studio's 'local'-debugger shows that there are other properties of return such as .Return and .Return.Value.Count as 4 (four).
My question is "what could be causing this"? All of my other rest-api calls and controller calls with Postman work correctly as one would expect.
Did I select the wrong type of "controller" from Visual-Studio? I am not experienced at all in coding classic MVC web-applications. VS-Blazor offer a number of controller-types. In the past, I "copied" a working controller and "changed the code" for a different "model".
Your assistance is welcome and appreciated. Thanks...John
I found out what actually happened to cause the result.Value is null and had nothing to do with the controller-type -- it was in the interpretation of the return value from the repository function.
I found an SO link Get a Value from ActionResult<object> in a ASP.Net Core API Method that explains how to respond to a ActionResult<objecttype> return value in the reply/answer section with the word "actual" is first defined. You will see this word "actual" in my revised code below.
My revised code is posted here with comments both inside the code section and below the code section. My comment inside the code begins with "<==" with text following until "==>"
// Initialize.
MOTRIP emptyMoTrip = new MOTRIP();
MOTRIP? resultMoTrip = new MOTRIP();
IEnumerable<MOTRIP> allTrips = Enumerable.Empty<MOTRIP>();
int daysPrevious = (int)(pTripType == eTripType.Any ? eDateRangeOffset.Week : eDateRangeOffset.Month);
// convert DateRangeOffset to 'dateonly' values.
DateOnly dtTo = DateOnly.FromDateTime( DateTime.Today);
DateOnly dtFrom = dtTo.AddDays(daysPrevious);
// Fetch the vehicle trips by date-range.
var result = await GetVehicleMOTripsByDateRange(UID_Vehicle.ToString(), dtFrom.ToString(), dtTo.ToString());
if ((result.Result as OkObjectResult) is null) { **<== this is the fix from the SO link.==>**
return StatusCode(204, emptyMoTrip);
}
var statusCode = (result.Result as OkObjectResult)!.StatusCode;
if (statusCode==204) {
return StatusCode(204, emptyMoTrip);
}
**<== this next section allows code to get the result's DATA for further processing.==>**
var actual = (result.Result as OkObjectResult)!.Value as IEnumerable<MOTRIP>;
allTrips = (IEnumerable<MOTRIP>)actual!;
if ((allTrips is not null) && (!allTrips.Any())) {
return StatusCode(204, emptyMoTrip);
}
<== this next section continues with business-logic related to the result-DATA.==>
if (allTrips is not null && allTrips.Any()) {
switch (blah-blah-blah) {
**<== the remainder of business logic is not shown as irrelevant to the "fix".==>**
Please use browser search for "<==" to find my code-comments.
Please use browser search for "actual" and "OkObjectResult" to see the relevant code fix sentences.

How to get images in the product list view with an API call in Shopware?

I’m working on a single-page-application with fully client-side rendered frontend (React) and Shopware acting as a headless CMS in the background. So all product-data will be pulled from the API and checkout will also use the API to send the data.
My problem is the following:
When trying to render the product list page, I call the articles endpoint which returns the basic info of all my products. The problem is that I would also need to render the main image associated with each product and this list does not expose any data from the media attached to the products.
I can get the media item(s) for a product using the Media endpoint, problem is that this one expects a Media Id which I cannot get from the listed representations of the articles.
So right now the only way I see to get the images is first making a call that gets all the products, then loop through them and get each product’s details, calling the article endpoint again with each product ID, then when I have the media IDs I loop through those and get the images for each product using the media endpoint, because that’s the only one that exposes the actual image path in the response. This seems way too complicated and slow.
Is there a smarter way to do this? Can I get Shopware to output the 1st image’s path in the Article list response, that’s associated with the current product?
Also I saw that the paths to the images look like this:
/media/image/f5/fb/95/03_200x200.jpg
The first part up to /media/image/ is fixed, that’s straight forward, and I get the image’s filename and extension in the article detail’s response in the media object and even the file extension, which looks like this in the API's response.
The problem is that I don’t know what’s the f5/fb/95/ stand for. If I could get this info from the details I could assemble the URL for the image programmatically and then I wouldn’t need the call to the media endpoint.
You can see how the path is generated in the class \Shopware\Bundle\MediaBundle\Strategy\Md5Strategy of Shopware's source code. The two relevant methods are:
<?php
public function normalize($path)
{
// remove filesystem directories
$path = str_replace('//', '/', $path);
// remove everything before /media/...
preg_match("/.*((media\/(?:archive|image|music|pdf|temp|unknown|video|vector)(?:\/thumbnail)?).*\/((.+)\.(.+)))/", $path, $matches);
if (!empty($matches)) {
return $matches[2] . '/' . $matches[3];
}
return $path;
}
public function encode($path)
{
if (!$path || $this->isEncoded($path)) {
return $this->substringPath($path);
}
$path = $this->normalize($path);
$path = ltrim($path, '/');
$pathElements = explode('/', $path);
$pathInfo = pathinfo($path);
$md5hash = md5($path);
if (empty($pathInfo['extension'])) {
return '';
}
$realPath = array_slice(str_split($md5hash, 2), 0, 3);
$realPath = $pathElements[0] . '/' . $pathElements[1] . '/' . join('/', $realPath) . '/' . $pathInfo['basename'];
if (!$this->hasBlacklistParts($realPath)) {
return $realPath;
}
foreach ($this->blacklist as $key => $value) {
$realPath = str_replace($key, $value, $realPath);
}
return $realPath;
}
Step for step, this happens generally to an image path, for example https://example.com/media/image/my_image.jpg, upon passing it to the encode-method:
The normalize method strips everything except for media/image/my_image.jpg
An md5-hash is generated out of the resulting string: 5dc18cdfa0...
The resulting string from step 1 is split at every /: ['media', 'image', 'my_image.jpg']
The first three character pairs from the md5-hash are being stored into an array: ['5d', 'c1', '8c']
The final path is being assembled out of the arrays from steps 3 and 4: media/image/5d/c1/8c/my_image.jpg
Every occurence of /ad/ is being replaced by /g0/. This is done, because there are ad-blockers which block requests to URLs containing /ad/
Shopware computes the path for you.
There is acutally no need to know about the hashed path...
You will be redirected to the correct path.
Try the following:
/media/image/ + image['path'] + image['extension']
An for the thumbnails:
/media/image/thumbnail/ + image['path'] + '_200x200' + image['extension']
or
/media/image/thumbnail/ + image['path'] + '_600x600' + image['extension']

How to overlay a cq widget so it is also available in SiteAdmin

The CQ.tagging.TagInputField provided two configuration parameter which won't work in combination:
tagsBasePath
namespaces
Using the OOTB facebook tags as example, I want to restric the dialog to only display the Favorite Teams. The Structure is this:
So I set tagBasePath to /etc/tags/facebook and namespaces to [favorite_teams]. This does what it is supposed to do and only shows the two teams in the dialog. But when you click on it, a JavaScript exceptions is thrown. The problem lies in the following method defined in /libs/cq/tagging/widgets/source/CQ.tagging.js
CQ.tagging.parseTag = function(tag, isPath) {
var tagInfo = {
namespace: null,
local: tag,
getTagID: function() {
return this.namespace + ":" + this.local;
}
};
// parse tag pattern: namespace:local
var colonPos = tag.indexOf(isPath ? '/' : ':');
if (colonPos > 0) {
// the first colon ":" delimits a namespace
// don't forget to trim the strings (in case of title paths)
tagInfo.namespace = tag.substring(0, colonPos).trim();
tagInfo.local = tag.substring(colonPos + 1).trim();
}
return tagInfo;
};
It does not respect the configurations set on the widget and returns a tagInfo where the namespace is null. I then overlayed the method in my authoring JavaScripts, but this is of course not working in the SiteAdmin as my custom JS are not included.
So, do I really have to overwrite the CQ.tagging.js below libs or can I somehow inject my overlay into the SiteAdmin so the PageProperties Dialog opened from there works as well?
UPDATE: I had a chat with Adobe support regarding this and it was pointed out that if you use tagsBasePath you need to place it somewhere else than below /etc/tags. But this won't work as well as the TagListServlet will return no tags as /etc/tags is also fixed in the TagManager as the tagsBasePath. I now overwrite the above mentioned js at its location, being well aware that I need to check it if we install a hotfix or an update. Is someone has a more elegant solution I'd be still thankful.

How do I avoid truncation of message subjects at 255 chars with the EWS managed API?

I have an email messge on an Exchange server (2010 SP1) with a Subject header that is 272 characters long. Both Outlook and OWA show it truncated to the first 252 characters followed by "...". EWSEditor shows it the same way. I know, however, that the full Subject is stored somewhere, because when I look at the headers in Message Options dialog Outlook or in the Message Details in OWA, all 272 characters are there.
My code is only gettting the truncated Subject, and I need a way to get the full string.
My code is using SyncFolderItems to get a ChangeCollection of ItemChange objects. I have two code branches for this. One retrieves FirstClassProperties, and one retrieves IdOnly. I have a function called getItemStringProp(), and depending on the branch, I either call it directly with the Item that I get from the ItemChange, or with the Item that I get by binding to the ItemChange.Item.Id. In both cases, my getItemStringProp() uses Item.TryGetProperty() and returns a max of 255 characters for the Subject. If the actual subject is longer, then I get 252 chars followed by "...".
Here's my code from the branch doing SyncFolderItems with FirstClassProperties:
useIdOnly = false;
icc = exchange.SyncFolderItems(folderId, PropertySet.FirstClassProperties, null, syncFolderItemsBatchSize, SyncFolderItemsScope.NormalItems, result.getSyncState());
and from the other branch:
useIdOnly = true;
icc = exchange.SyncFolderItems(folderId, PropertySet.IdOnly, null, syncFolderItemsBatchSize, SyncFolderItemsScope.NormalItems, result.getSyncState());
Following this, I drill down to get the Subject:
foreach (ItemChange ic in icc)
{
if (!useIdOnly)
{
icSubject = getItemStringProp(ic.Item, EmailMessageSchema.Subject,"Subject", folderName,"");
}
else
{
PropertySet itemProps = new PropertySet(BasePropertySet.IdOnly);
itemProps.Add(EmailMessageSchema.Subject);
itemProps.Add(EmailMessageSchema.DateTimeSent);
itemProps.Add(EmailMessageSchema.ItemClass);
Item item = Item.Bind(exchange, ic.Item.Id, itemProps);
icSubject = getItemStringProp(item, EmailMessageSchema.Subject, "Subject", folderName, "");
}
}
And here's the function that gets the Subject:
private String getItemStringProp(Item item, PropertyDefinition propDef, String propName, String fName, String defaultValue)
{
// some debug logging code and error checks omitted
object prop = null;
String value = "";
try
{
if (item.TryGetProperty(propDef, out prop) && prop != null)
{
value = prop.ToString();
}
if (prop == null || value == null)
{
value = defaultValue;
}
}
return value;
}
By the way, I'm aware that neither Outlook (at least the 2007 version) nor OWA allows creation of a message with a Subject longer than 255 characters. The message in question came into Exchange via SMTP, and a Subject far longer than 255 characters is legal according to the RFCs.
Don't rely on Item.Bind(), sync, search, or any other operation in EWS to load up all of the properties you're looking for. Have you tried getting the item, then doing a .load(PropertySet) or ExchangeService.loadPropertiesForItems()? Some properties won't come through in various retrieval actions even if you specifically request them. Some may come through, but get truncated. What makes it more fun is that I don't think there's any documentation telling you exactly which operations will return which properties, so you get to guess and check. You have to load the property set after you retrieve the Item(s), so it's usually best to get the Item with the ID only, then load the property set.

Sequence of Project Deployment in Mule

Does anybody know in what sequence, the mule project are loaded when the Mule starts-up?
It doesn't seem to be in alphabetical order or last updated time.
If you look at the class MuleDeploymentService, you can see the following:
String appString = (String) options.get("app");
if (appString == null)
{
String[] explodedApps = appsDir.list(DirectoryFileFilter.DIRECTORY);
String[] packagedApps = appsDir.list(ZIP_APPS_FILTER);
deployPackedApps(packagedApps);
deployExplodedApps(explodedApps);
}
else
{
String[] apps = appString.split(":");
Description for method File.list states that "there is no guarantee that the name strings in the resulting array will appear in any specific order". So, I guess the answer is in no particular order, or in the order they are listed in using the -app option.