wagtail 'Page with this Path already exists.' error on attempting to create page manually - wagtail

I have some code to create a Custom page object as part of a data import:
instance = PerformancePage(
run=run,
date=json_data['date'],
time=json_data['time'],
price=json_data['price'],
title=f'{run.title} {json_data["date"]} {json_data["id"]}',
content_type=ContentType.objects.get_for_model(PerformancePage)
)
perf = run.add_child(instance=instance)
and that sometimes raises:
django.core.exceptions.ValidationError: {'path': ['Page with this Path already exists.']}
A bit of debug code does show that there is another page out there with the same path:
except ValidationError:
print('error attempting to save', instance)
print('path', instance.path)
print('is leaf', run.is_leaf())
rivals = Page.objects.filter(path=instance.path)
print(rivals.last().specific.run == run)
Why might that be?
trying to manually increment the rival path to set a new path doesn't work either:
instance.path = rivals.last().specific._inc_path()
perf = run.add_child(instance=instance)
# still raises
Interestingly, if I just skip over those exceptions and continue my import,
when I print out those paths, they seem to follow a similar pattern:
path 00010001000T0005000D0001
path 00010001000T000800050001
path 00010001000T000900060001
path 00010001000T000A00050001
path 00010001000T000A00050001
path 00010001000T000A00070001
path 00010001000T000A00070001
path 00010001000T000A00030001
could that be relevant?

Looks like the in-memory parent "run" object was out of date. re-fetching it from the database before trying to add the child fixes the problem:
run = RunPage.objects.get(id=run.id)

Related

Drupal upload then move doesn't update path or filename in file list

I have a custom form using a "managed_file" which uploads to temp folder. Programmatically, I then load that file and move it to its permanent storage (overwriting any existing file with the* name) e.g.
// Upload file
$upfile = $this->entityTypeManager->getStorage('file')->load($fid);
// Source and destination
$sourceUri = $this->fileSystem->realpath($upfile->getFileUri());
$destinationUri = $this->fileSystem->realpath(\Drupal::config('system.file')->get('default_scheme') . "://") . '/x/y/z/XYZ_NEW.pdf';
// Move and overwrite
$this->fileSystem->move($sourceUri, $destinationUri, FileSystemInterface::EXISTS_REPLACE);
All of this works (i.e. the file physically is moved into the correct place with correct name); however, the file displayed in the listings (i.e. /admin/content/files) still shows the incorrect temporary folder as the URI.
Basically the file in the listings page seems to be showing the original filepath and name* of a previously successfully moved* file.
If I load this file with incorrect URI, i.e. using the incorrect temp path, the data loads, but then will not have a file info (as it doesn't exist. Also clicking the filename by browser under listings will show page not found and the URL showing the old URL i.e. /system/temporary?file=XYZ.pdf).
If I load this file by correct URI, i.e. using the correct destination path, file not found - same if I go to the path directly in the browser.
It appears the managed file doesn't know it was moved. How to resolve this bug?
The docs for FileSystem::move say "Moves a file to a new location without database changes or hook invocation."
So you are going to need to update the file entity with the new values..
Try this, untested:
// Upload file
$upfile = $this->entityTypeManager->getStorage('file')->load($fid);
// Source and destination
$sourceUri = $this->fileSystem->realpath($upfile->getFileUri());
$destinationUri = $this->fileSystem->realpath(\Drupal::config('system.file')->get('default_scheme') . "://") . '/x/y/z/XYZ_NEW.pdf';
// Move and overwrite
$newFileName = $this->fileSystem->move($sourceUri, $destinationUri, FileSystemInterface::EXISTS_REPLACE);
// Set the new file path on the file entity.
$upfile->setFileUri($newFileName);
// Set the file to permanent if needed.
$upfile->setPermanent();
// Save entity with changes.
$upfile->save();
I did not test this though.
You can check the functions on the file entity in the docs here
It turns out the class based methods do not update the database
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21File%21FileSystem.php/function/FileSystem%3A%3Amove/8.9.x
The procedural version does
https://api.drupal.org/api/drupal/core%21modules%21file%21file.module/function/file_move/8.9.x

CakePHP - Router::url() generating url relative to current page

I'm trying to replace all of my static routes with the CakePHP 3.8 Router::url() method.
On my local environment, when accessing the the below code from the manage/lender-products/read/2 route, I get the expected results of /manage/lenders from the below code:
\Cake\Routing\Router::url(['controller' => 'Lenders', 'action' => 'index'])
However, on production (bref.sh/AWS Lambda/PHP 7.4), I get the unexpected result of /manage/lender-products/read/manage/lenders.
It appears that in my production environment, the URL is being generated and is including the current pages url in the end result.
In case you do not set a custom base path for the App.base config option, CakePHP will try to figure the possible base path out on its own, based on $_SERVER['PHP_SELF'] and $_SERVER['DOCUMENT_ROOT'], so it's likely that one or both of these contain unexpected values in your AWS environment.
The simple fix would be to manually set the base path in your config/app.php. If your application doesn't actually live in a public subdirectory (ie in case webroot is your document root), then you should set the path to an empty string, otherwise provide the path, like /my/app (with a leading slash, but without a trailing slash!).
See also
Cookbook > Configuration > General Configuration

How can I find the path of the current gradle script?

We use some Gradle base scripts on an central point. This scripts are included with "apply from:" from a large count of scripts. This base scripts need access to files relative to the script. How can I find the location of the base scripts?
Sample for one build.gradle:
apply from: "../../gradlebase/base1.gradle"
Sample for base1.gradle
println getScriptLocation()
I'm not sure if this is considered an internal interface, but DefaultScriptHandler has a getSourceFile() method, and the current instance is accessible via the buildscript property, so you can just use buildscript.sourceFile. It's a File instance pointing at the current script
I'm still not sure if I understood the question well but You can find path of current gradle script using following piece of code:
println project.buildscript.sourceFile
It gives the full path of the script that is currently running. Is that what You're looking for?
I'm pulling it off the stack.
buildscript {
def root = file('.').toString();
// We have to seek through, since groovy/gradle introduces
// a lot of abstraction that we see in the trace as extra frames.
// Fortunately, the first frame in the build dir is always going
// to be this script.
buildscript.metaClass.__script__ = file(
Thread.currentThread().stackTrace.find { ste ->
ste.fileName?.startsWith root
}.fileName
)
// later, still in buildscript
def libDir = "${buildscript.__script__.parent}/lib"
classpath files("${libDir}/custom-plugin.jar")
}
// This is important to do if you intend to use this path outside of
// buildscript{}, since everything else is pretty asynchronous, and
// they all share the buildscript object.
def __buildscripts__ = buildscript.__script__.parent;
Compact version for those who don't like clutter:
String r = file('.').toString();
buildscript.metaClass.__script__ = file(Thread.currentThread().stackTrace*.fileName?.find { it.startsWith r })
Another solution is set a property for the location of A.gradle in your global gradle settings at: {userhome}/.gradle/gradle.properties
My current workaround is to inject the path from the calling script. This is ugly hack.
The caller script must know where the base script is located. I save this path in a property before calling:
ext.scriptPath = '../../gradlebase'
apply from: "${scriptPath}/base1.gradle"
In base1.gradle I can also access the property ${scriptPath}
You could search for this scripts in the relative path like:
if(new File(rootDir,'../path/A.gradle').exists ()){
apply from: '../path/A.gradle'
}
This solution has not been tested with 'apply from', but has been tested with settings.gradle
Gradle has a Script.file(String path) function. I solved my problem by doing
def outDir = file("out")
def releaseDir = new File(outDir, "release")
And the 'out' directory is always next to the build.gradle in which this line is called.

How can I intercept the file:// requests to provide a directory listing?

I'm trying to replace the directory lists generated by Firefox by a custom one.
So, I need three things:
I need to know when Firefox tries to load a file:// URI.
I have to test if that URI targets a directory or a file.
If the target of the URI is a directory, i have to prevent Firefox from generating the directory listing, and show my own directory listing instead.
It's the first point that is the most problematic:
I read the documentation of the nsIObserverService but it doesn't do what I want (there is a http-on-modify-request but no file-on-modify-request)
I tried to use the Jetpack's addon-kit/page-mod on file://* URIs, but it seems it doesn't allow me to verify if the URI targets a directory before loading my stuff.
I read the documentation of the nsIFileProtocolHandler, but it doesn't help me.
So how can I intercept the file://* requests?
Does somebody have an idea?
I tried to use the Jetpack's addon-kit/page-mod on file://* URIs, but it seems it doesn't allow me to verify if the URI targets a directory before loading my stuff.
That's right. However, you can do the same thing that page-mod module is doing, namely listen to the document-element-inserted observer notification. Something along these lines:
var events = require("sdk/system/events");
var urls = require("sdk/url");
events.on("document-element-inserted", function(event)
{
var window = event.subject.defaultView;
if (!window) // XBL document?
return;
var url = urls.URL(window.document.URL);
if (url.scheme == "file")
{
// A file:/// URL was loaded, do something with this window
}
});
For reference: system/events module, url module.
I have to test if that URI targets a directory or a file.
The url module also allows you to get the file path, and then you can use the io/file module to access it:
var files = require("sdk/io/file");
var path = urls.toFileName(url);
var isDir = files.exists(path) && !files.isFile(path);
If the target of the URI is a directory, i have to prevent Firefox from generating the directory listing, and show my own directory listing instead.
That's the hard part. I don't think that you can prevent Firefox from generating the directory listing but calling window.stop() should have the same effect. You can then dynamically add your own contents to the window. You can use files.list(path) method to get a list of directory entries:
window.stop();
var entries = files.list(path);
for (var i = 0; i < entries.length; i++)
addEntry(window, path, entries[i]);

Qt SQLite database without absolute path

Is there a way to reference the database.sqlite file without knowing the absolute path?
_db = QSqlDatabase::addDatabase("QSQLITE");
_db.setDatabaseName("/the/path/i/dont/know/database.sqlite");
I already tried to add the database.sqlite to the Resources folder and call it via qrc:, but apparently it is not possible to write to a resource file.
I also tried using QApplication::applicationDirPath();, but this would result in different paths depending on the user's OS. E.g. it appends MyApp.app/Contents/MacOS to the actual directory.
When you create a QSqlDatabase with SQLite as a backend you have two options:
Give an absolute path as a db name
Give a relative path: in this case the database will be saved in the directory of your binary.
So you must know absolute path of your db in your case.
edit
In the case you initially know where the database should be located you can either hardcode it (which is never wise) or you can create a configuration and load it using QSettings. For example:
QSettings settings;
QString dbPath = settings.readValue("DBPath", QString(/*fallback path*/)).toString();
//do smth with dbPath
Take a look further here
if you want to store the db per user you shout use this:
QDesktopServices::storageLocation(QDesktopServices::DataLocation)
this method returns the location where persistent application data can be stored.
for more information check this: http://doc.trolltech.com/4.5/qdesktopservices.html#storageLocation

Resources