marcus_johansson → credited tolstoydotcom → .
Look up the Drupal Slack channels. You want the project-browser channel.
IMNSHO, Project Browser encourages an unsafe installation. I'm working on a safe alternative and I'll announce it there and on Reddit. It'll take some time.
They haven't made it clear enough, but "Drupal Core" and "Drupal CMS" are basically the same thing. CMS just has pre-installed modules and recipes.
Not to further confuse things, but see the "dangerous" part: https://wisdomtree.dev/drupal-cms-review
Someone else had the same problem recently:
https://drupal.slack.com/archives/C1BB308HH/p1737368679086469
Before I knew about this issue, I suggested this:
It would help if you could edit drupal_check_module(). Just before the "addError" line, add this:
echo '<pre>';print_r($requirements);exit;
Then, post what it prints. It might warrant a bug report somewhere.
Even if the module is missing dependencies, it doesn't seem like drupal_check_module() should be doing this.
Drupal runs fine if you just install MySQL (or even Postgres) and a web server (Apache2 is easier; if you use nginx you'll need to find a config file).
It doesn't really require that, it just doesn't highlight alternatives.
I'm on Ubuntu and instead of ddev etc, I just use apache2 + MySQL. Apparently Ubuntu can be installed on Windows so that's an option.
I'm currently working on a way to make dealing with composer easier for non-devs.
If you use section names like "admin >> editing" and "admin >> deleting", it will look like a hierarchy.
Or, sections can be nested (the client would have to be aware of that of course):
sections:
- title: users
weight: 1
extended_help: 'permissions for regular users'
- title: admin
weight: 2
extended_help: 'permissions for admins'
sections:
- title: editing
weight: 1
extended_help: 'permissions for admins editing items'
- title: deleting
weight: 2
extended_help: 'permissions for admins deleting items'
I don't have an answer for that, but I didn't mean for you to make my change permanently. "Don't hack core" is an old maxim around here. What's $plugin_id?
I pushed the changes/additions if anyone wants to review them. I made a minor change to the existing file core/modules/user/src/PermissionHandler.php and a few changes to core/modules/user/user.services.yml (I added comments for those).
There are now three ways to get permissions:
- the 'user.legacy_permissions' service. This is the current way.
- the 'user.permissions' service. This is a replacement for the former way.
- the 'user_permissions_parser.repository_factory' service. This is what new code should use, it returns an object with all of the permissions.
There are differences in the sorting between the first two services above. If that's an issue I can change it. However, both get the same permissions.
I can add tests if this looks OK.
The code in question:
if (isset($definitions[$plugin_id])) {
return $definitions[$plugin_id];
}
What I'd do is use my rawdebug module and print out what's in $plugin_id and get a stack trace. At least add echo (string) $plugin_id;exit;
and let us know what it says. A quick fix is probably to replace $plugin_id
with (string) $plugin_id
in both places but I'd only do that if I thought it was just a matter of having to clear the cache or similar.
You could write a script to run composer commands over the web, but that would be very unsafe: you'd basically be installing a tool (a shell) that hackers try to install on your site.
cPanel has a shell but that depends on your host.
Drupal could work with cPanel and similar to help people like you, but my suggestion to do that has been unilaterally ruled out: https://www.drupal.org/project/drupal_cms/issues/3491208 ✨ Create installation recipes that cPanel & hosting providers can use Active
The alternative is to work on your site locally and then upload it to your hosting. ddev is one option, but I just use local apache2 and mysql installations. I'm on Ubuntu so this is a lot smoother than Windows.
I might be and probably am wrong, but that trace doesn't offer a good clue. Are you able to export the config files using either the UI or drush? Is that route in those files somewhere?
If you can answer that and post this to the core issues queue they might be able to spot something.
An idea I have is some sort of "safe mode" for Drupal that would provide replacement versions for files that, instead of throwing exceptions, return anodyne data where possible (as with the help route in my previous suggestion).
Can you help find out the reason?
- make a backup of RouteProvider.php
- change line 211 to echo '<pre>';debug_print_backtrace();exit;
- post the result (use a storage service since it'll be a huge file)
Then, undo those changes and change line 211 to return $this->getRouteByName('help.main');
Then, clear caches/run update.php/run cron a couple times to see if that solves it.
Note: If you can avoid doing the last until later it might be helpful since there might be other things you can do to figure out the problem.
Every contrib module has the necessary composer command. The other main one you need is 'composer update'. If something goes wrong, there's a composer forum here.
If you can find the cause of the WSOD that would help. What does it say on admin/reports/dblog?
I did 'composer update' and uninstalled AI Search. All I get for admin/config/ai/explorers/vector-db-search is a 404 and there's no route with vector-db-search in the current code. I didn't see the strings in my changes in the current code either. Since I haven't been keeping up on all of the intervening commits, what are the steps I should be testing?
Assuming this is just a temporary issue that a cache clear + cron + update.php will solve, then you can temporarily replace the given line with this: return $this->getRouteByName('help.main');
Note that that could cause an infinite loop.
It'd be better if you can find out what's looking for that route by calling debug_backtrace().
I did as #6 suggests.
No matter how it's presented, augmenting permissions files seems like it's going to be necessary for new users. Should I create a patch? If so, should it be based on the current module, or based on using plugins for each of the additions to the permissions files. Those plugins would have a weight so the heaviest plugin would do what currently happens: everything is assumed to be a permission. Each plugin would get an array derived from the permissions file and the plugin would indicate in that array if what it's processed.
https://www.drupal.org/project/tokenuuid → isn't unmaintained, I just pushed a D11 version. I was waiting on https://www.drupal.org/project/tokenuuid/issues/2928359 📌 Why is this not a patch for Token? Postponed but when this patch is backported I assume tokenuuid can be marked as not needed.
Thanks, but this module is going away since the dev version of D11 has the same functionality. That will eventually be backported.
I updated it for D11. However, bear in mind this module is going away since the dev version of D11 has the same functionality.
tolstoydotcom → created an issue.
I'm sure there's a way to do this with Views, but the quicker option might be some custom code. Another option is to denormalize the data, like give City text fields for Country and Continent and then populate those when a City is created.
It's good you found the problem. Can you repost your solution to https://www.drupal.org/project/issues/opigno_module → ?
There's a D10 version of tb_megamenu. If that's already installed they might have changed the name of their entity type. You might need to do some database surgery or use a module that lets you change machine names (if there is such a thing).
The missing field can be added, but you'll then need to import the data for it.
I can't help with CSS issues but those on Fiverr might be able to help.
To find out what the 500 error is, look in admin/reports/dblog. It's probably related to the missing field or similar.
I don't know why you're changing the directory name. While it's probably not related, do you still have the problem if you don't do that?
Can you add debugging code to find out what's generating the redirect? Like adding debug_print_backtrace() in the file you mentioned.
What errors are you getting? Don't they give you some ideas as to what needs to be fixed?
If you have a budget for this you can hire me or someone else to do this for you, otherwise you should ask to determine if the errors can be easily fixed or if they indicate serious problems.
All I see is a red "X". First you need to find out how ads are being served. An external service, or from Drupal itself? If the latter, look through admin/content or admin/structure/block to see if you can find it. If your org has a budget for this, you can reach out to me or others here for paid help with such issues.
This works with D10: https://www.drupal.org/project/give →
There are also donations add-ons for Commerce.
Or, if there's a D7 donations module you prefer and you have a budget to make it D10 compatible, feel free to DM me.
Disable unnecessary modules and use different themes to try to isolate where the problem is coming from. If there's custom code anywhere, disable that. Then, if there's still a problem, file this as a bug report for that specific project.
FYI, I'm proposing adding new keys to permissions.yml files that would add help text and divide permissions into sections:
https://www.drupal.org/project/drupal/issues/3495351 ✨ Make the user permissions page less daunting for new users Active
These two issues are related, but this issue concerns the UI and mine mainly concerns the files.
The two issues are related, but I don't think this is a duplicate: the other issue is about how to present the info, this issue is about giving more info. As with art, I know what UX I like but I can't draw a crooked line so I'm perfectly willing to let others handle that aspect of things. No matter what it ends up looking like, the permissions page is going to need more info if anyone expects regular users to use it. Regular users are going to need much more detailed explanations of what various things do.
And, there needs to be some sort of structure rather than confronting them with dozens of permissions from one module. Field UI has 28 permissions and Node has 42. That's going to overwhelm people and dividing the permissions into sections would make it easier for newer users. One of the node permissions is "Bypass content access control"; regular users aren't going to understand what that means, the side-effects, etc etc without a detailed explanation and examples.
tolstoydotcom → created an issue.
I've written code to parse the yml format I gave above and I'll release it as a contrib module within a week or so.
This does not necessarily require changes to core because the parser class I'm replacing (PermissionHandler) is a service (user.permissions) and, unless something's doing something it shouldn't, I should be able to override that. I'll duplicate the code to show the permissions page and replace that path.
Hopefully the core team will see the utility of a much less cryptic permissions page.
My suspicion is this is about annotations not being parsed correctly. If this issue happens to anyone else, please test out the suggestion at the end of #39.
An additional idea is to add sections. For instance, the 'Access any webform configuration' permission could go in an 'admin' section while 'Edit own submission' could go in a 'user' section. Sections would have weights and extended_help. Permissions would also have weights that would control their position within its section. 'section_callbacks' would be similar to 'permission_callbacks'; if both 'section_callbacks' and 'sections' are provided, then sections from the callback(s) are appended to 'sections'.
All of these changes would be optional so current *.permissions.yml files would continue to work, they'd just have one section and no extended_help.
If enough are interested I can make a patch for PermissionHandler::buildPermissionsYaml() to add these optional items to the return from PermissionHandler::getPermissions(). I'd probably use a helper object in buildPermissionsYaml() but the return value of getPermissions() would stay the same. Then, these changes could be worked into the UI.
main:
extended_help: These permissions deal with the webform module. You can probably use the default settings, however, if you want to do [ABC], you might want to [XYZ]. Another possible use case is [ABC2]. In that case you'd [XYZ2].
section_callbacks:
- Drupal\xyz\ABC::xyz
- Drupal\xyz2\ABC::xyz2
sections:
- name: admin
weight: 1
extended_help: something, something, and something
- name: users
weight: 2
extended_help: another thing, another thing, another thing
'access webform overview':
title: 'Access the webform overview page'
section: 'admin'
weight: 1
description: 'Get an overview of all webforms.'
extended_help: Users with this permission can view the page [ABC]. They'll be able to see [XYZ], [ABC2], and [XYZ2]. So, you should only give this permission to users who need to see [ABC].
tolstoydotcom → created an issue.
One possibility is to augment *.permissions.yml files so they have "extended_help" sections. For instance, webform.permissions.yml would have:
main:
extended_help: These permissions deal with the webform module. You can probably use the default settings, however, if you want to do [ABC], you might want to [XYZ]. Another possible use case is [ABC2]. In that case you'd [XYZ2].
'access webform overview':
title: 'Access the webform overview page'
description: 'Get an overview of all webforms.'
extended_help: Users with this permission can view the page [ABC]. They'll be able to see [XYZ], [ABC2], and [XYZ2]. So, you should only give this permission to users who need to see [ABC].
There would be the standard question mark in a circle image that users could mouse over to see the extended_help in a popup.
Look in the browser console, the image URLs are probably bad.
I made some minor changes to what's in git and the pages load. I don't know if it will work or not but you can find out:
mkdir temp
cd temp
[put the patch file in temp]
git clone git@git.drupal.org:project/paypal_roles.git
cd paypal_roles
git checkout remotes/origin/8.x-2.x
patch -p1 < ../paypal_roles-2633302-10.patch
Look on admin/reports/dblog. There will also be logs under /var/log/apache2 or similar, but you might need to ask your host to see those.
Those replies raise several questions. Where has it been discussed? Is the discussion open?
"The details will be posted when we have them." Who will supply those details?
I'm not suggesting focusing on cPanel to the exclusion of others. I don't use such control panels and I'm not familiar with their competitors, so read "cPanel" as "cPanel, Plesk, or whatever". But, will such control panels be urged to provide what I described?
Will those "hosted solutions" just be Acquia, Pantheon, and perhaps some other insiders, or could major hosting companies like 1&1, Blue Host, GoDaddy, etc make it part of their offerings? The idea of just reserving this for "Drupal hosts" is self-marginalizing.
tolstoydotcom → created an issue.
Can you search all of the Drupal files for 'logduty'? Maybe it's a contrib module.
If you don't find it, and you don't let visitors register accounts, maybe you got hacked. Does the user have elevated permissions?
It'd be a good idea to replace this, but now there's an SSH class that has things like ssh2_exec($this->connection, 'rm -Rf ' . escapeshellarg($directory))
. There's no check if $directory is '/', '/bin', 'bin', '/home/*', etc. That code is in the removeDirectoryJailed
method so presumably it's only allowed to operate within a specific directory.
Also, the vendor directory for D11 dev is chock full of calls to exec()
and shell_exec()
. Symfony even includes an .exe
file.
I tried this on a version of D11 dev with 2.0.8 of ECA (I enabled its Content/Core/UI/Views submodules) and I don't see the same issue. In one of the other posts you said it was due to Taxonomy Manager, is that still the case?
Can you start from a blank slate and give a text description of the steps to reproduce this?
Have you tried with other browsers and disabling any extensions in those browsers?
tolstoydotcom → created an issue.
If you do that as it is, someone could hijack your site and your visitors' computers: CSS can load URLs. If you really want to allow this, I'd suggest either a) having an admin with deep CSS knowledge review each submission before allowing it to be published (and make sure the admin doesn't load the CSS in the page, only in a textarea), or b) running it through a CSS parser and having a whitelist of what's allowed.
I assume the new hosting uses a MySQL version that D7 doesn't support. IIRC, every D7 query goes through one function in mysql.inc or similar so you should be able to do a search and replace there.
D9 is no longer supported. What's keeping them from moving to D10? It shouldn't be that difficult.
hongpong → credited tolstoydotcom → .
Please file a bug report in the advagg issue queue.
I based this on the latest dev version. I injected services in the last two places I listed in #23. I didn't deal with the drush code. It'd be great if we could freeze changes to dev for a while, test the patch on D9/D10/D11 (I only tested in on D11), and then deal with drush etc.
To use:
git clone git@git.drupal.org:project/wordpress_migrate.git
cd wordpress_migrate
patch -p1 < ../d11update-3435726-26.patch
I have the dev version of D11 installed and the Drush version is 13.2.0.0. So, that might indicate an issue.
The other thing that I wonder about is #3. It'd probably be better to change the web server config to serve out of the 'web' directory. If you can't do that, move the installation so index.php etc are in 'public_html' and 'vendor' is above it. You'll probably need to change 'web' to 'public_html' in composer.json and composer.lock. But, it'd be better to just change the web server config.
If there's still a problem, look at any files you changed. They might have something that's printing something out inadvertently. There might be a closing PHP tag followed by whitespace. Siteground might also be running something that causes output.
I count five instances of \Drupal:
- in README.md. I don't think that should be replaced
- in a drush function. Unless that's changed to use classes, that can't be injected.
- in SourceSelectForm. I didn't inject that because getFileValidators will be reworked so the Drupal call is no longer needed once the module doesn't support D9
- in ImportWizard. I don't know if there will be a conflict with how ctools does things. If there is, this might have to remain.
- in ImageSelectForm. I can replace that.
I'll post another patch over the weekend that deals with the last two items.
I haven't tested this, but do a find for function system_status
Then, change the line to be $requirements = module_invoke_all('requirements', 'runtime'); print_r($requirements);exit;
That might show you the problem. Just make sure to change the line back.
Release it to the public and solicit feedback or pay someone (such as yours truly) to do it.
If you have *specific* concerns about a contrib module (like unsafe code you see in it), privately contact the Drupal security team: https://www.drupal.org/drupal-security-team →
If you don't have specific security concerns and it's a contrib module, what's the issue?
AFAIK, the link in my last comment is where the latest code is. If anyone wants to test it, just:
git clone git@git.drupal.org:project/wordpress_migrate.git
cd wordpress_migrate
patch -p1 < d11update-3435726-13.patch
If you clone https://git.drupalcode.org/project/wordpress_migrate the patch should apply cleanly.
I can't say that for certain because when I've used them it's been in conjunction with contrib or custom code that determines if the user has access to the file. I'd suggest just using that contrib or custom code.
Have you tried 'drush cc' and then choosing the Drush cache? You can also truncate 'cache_' tables in the db, run cron, run update.php, clear cache, etc.
Read the comments before that line of the file. You need a module that implements hook_file_download and decides if the user can access that file. I believe that'd be something like private_files_download_permission
I'm not an SEO and search engines have their own ways of doing things, but if you keep the URLs the same and avoid 404s and have the same features in the page (like site-specific metadata), it seems like that should be enough. This module might help: https://www.drupal.org/project/seo_checklist →
The design question is a little tricky since Drupal and WP themes use different technologies. If your WP theme is based on Bootstrap that would probably make it easier. If your WP theme has a lot of customizations that'd make things more difficult. There are carousels and other types of widgets for Drupal if you're using those with WP. In any case, a good CSS person should be able to make the themes look the same.
If you have a budget for this I can help you set up the site and do the migration.
Here's an updated patch. It should work with D9/D10 but I only tested D11 this time.
I can rework my patch to have the missing parts, it does work on D11.
However: the rector patch uses the backwards compatibility layer. My patch instead tests if things are available. That's not an issue if you don't want to support D9.
Should I post a new patch?
That's odd, I'll update it tomorrow. I'll also test out #5 on the other thread.
An abridged def is:
namespace Drupal\wordpress_migrate;
interface WordPressMigrationGeneratorInterface {
public function createMigrations(array $configuration): void;
}
and the services file:
services:
wordpress_migration_generator_factory:
class: Drupal\wordpress_migrate\WordpressMigrationGeneratorFactory
arguments: ['@entity_field.manager', '@plugin.manager.migration', '@file_url_generator']
Are the tags being written to the search index?
You could also have a list of tags, a tags cloud, use faceted search, etc.
This appears to be a common issue, see
https://www.drupal.org/forum/support/upgrading-drupal/2024-11-07/1036-to... →
In Twig 3.14.0 (used by a version of D11 dev), that method is just this.
I don't know if this version relies on something that's not in earlier versions, but you can always try swapping this in. If that fixes it, then try to upgrade to a version of Drupal that uses a higher version of Twig, or just make the change each time you upgrade something.
public function ensureToStringAllowed($obj, int $lineno = -1, ?Source $source = null)
{
if ($this->isSandboxed($source) && $obj instanceof \Stringable) {
try {
$this->policy->checkMethodAllowed($obj, '__toString');
} catch (SecurityNotAllowedMethodError $e) {
$e->setSourceContext($source);
$e->setTemplateLine($lineno);
throw $e;
}
}
return $obj;
}
You'll need to provide more info. What's the code around that line? What are the values of the variables involved?
Have you tried running cron, clearing the cache (if necessary, in the db), and running update.php?
Hopefully that will be clearer for non-native speakers.
It's probably a MySQL setting related to packet size, timeouts, etc.
Why not just use mysqldump?
You should post how you solved it. Did you visit admin/reports/dblog? Look in the watchdog table in the database? Look through the web server's error log?
It would help if you could indicate the classes & methods involved in a 404.
As a matter of policy you might want to rethink this. Using a VPN doesn't necessarily mean the user is up to no good. For banks etc blocking VPNs might make sense, but the reason Google/Facebook/etc look askance at them is because they want to compile dossiers on people and follow them around the internet.
What's $callback
before the call to doTrustedCallback
? This sounds like it could be a difference in PHP versions, but it might also be a data issue.
I was able to duplicate the issue. The patch applied and looks OK.
One question is whether using a non-existent format should thrown an exception or not. Given the use of a fallback format, it seems like the idea is that a formatted date should be returned even if a bad format is used. So, this seems OK to me unless someone suggests logging a warning the first time a non-existent format is used.
Are you saying you're getting this on a *fresh* install? If so *and* you meet all the requirements, *and* you can duplicate this and document the steps, then file this in the core issue queue.
But, since this is D10, I suspect you have some sort of... unmet dependency. You'll need to look in the server error log to find out what it is. Or, print out $invalid_default_config and $missing_dependencies in ConfigInstaller::checkConfigurationToInstall().
What are the links on admin/structure/taxonomy? It's probably not great to use URLs like taxonomy/term/%ID%, instead you'd want something more SEO friendly. But, if you want those and they aren't like that, you can write code to change them.
The mail thing is a key bit of info. If you can't get your hosting to enable mail, then you'll need to install something like maillog to prevent mail from being sent.
The issue appears to be the error. You've probably got a message with a red "X" or similar on the page I mentioned. You need to find that, or an error in your server logs.
If you have a budget for this I can look at the admin side of the site. It shouldn't take long to find the error, but fixing the error may or may not take a while.
It's probably the "unexpected error". Are there errors listed on admin/reports/dblog or in the web server error log?
There's no way to tell since you haven't provided enough info. When you go to admin/config/development/configuration/single/export and export a role, is everything listed in there available on the target site?
Can you write code to ensure the role is actually set, or use migrate_devel to show the destination?
If you write code to set a role programmatically outside the migration, does it work or throw an error?
Since others haven't added to this issue, I assume it has something to do with your environment. Can you disable custom/contrib modules one by one and see if the problem keeps happening? Some code somewhere is probably trying to store the database connection in the db or similar.
What I'd do is create a test setup with just a couple of sites and then add code that logs what the update module is doing.
If you don't get much traffic and your cache is 10gigs, I'd first figure out what's filling the tables.
Setting up Redis or memcache is straightforward. Your host might already have one of them installed, or you can do it with SSH access. But, you're going to need a lot of RAM.
I'd figure out a better way to do caching based on what's being cached.
You can also clear all the tables starting 'cache_' using 'TRUNCATE ' or similar. You might delete something someone was just using, but unless you take the site offline for a bit that can't be helped.
Feel free to close if you still don't want to resolve this.
You can duplicate this by creating a node type with a reference to media entities. Then, create a node of that type and add images. Photoswipe should work OK. Then, edit the node and add a video. You'll get the crash above.
In my case, $this->settings['photoswipe_reference_image_field']
in ImageDTO's constructor is field_media_image
, but $this->getMediaSourceField($media)
is field_media_video_file
for the video. Thus, $media->get($photoswipe_reference_image_field);
crashes.
It's easier for me to just add a separate video field to my node type, but you might want to skip media types that you aren't expecting.
Why delete the node at all? If Hoarding: Buried Alive has taught us anything, it's that we should Just mark it with some special sign (like a 'Not completed' term) but keep it around so you have a record
[Copying from the since closed "Hiding the node title makes node form submission crash 🐛 Hiding the node title makes node form submission crash Active "]
It seems to me that everything should have a title, even in the case of a "Person" object. The title could be random, or constructed from something else, but you should always be able to count on an existing entity having a title of some kind just as you can count on it having an ID.
Also, I don't think handling this at the form level would be a good idea since someone might hide the title and then have custom code or a contrib module that provides the title. Preventing them from hiding the title would cause problems for them. If you let them do it but instead show a warning that would annoy people who know what they're doing and confuse others.
I suggest changing SqlContentEntityStorage::mapToStorageRecord to throw an exception if a field is required but empty. It already throws an exception in one case throw new EntityStorageException("Table mapping contains invalid field $field_name."); so adding a check in the same area doesn't seem out of place. You're going to get an exception in any case, it might as well give the user a clue what the problem is.
The problem you're talking about isn't clear enough, but for such cases you can try to access a file like README.md. Adjust your web server settings or file permissions until you can read that in the browser.
Is bootstrap_layout_builder installed and enabled?
I had this issue on Drupal 11.0.4 and it was because there was no language.entity.en
config entity. There were such entities for various other languages, just not en
. I have no idea how that happened.
Just because it might mask an underlying issue is no reason not to make the updateLockedLanguageWeights
method more bulletproof.
I've attached my attempt to do that based on D11 dev of Oct 18, 2024.
Please send me an email via my profile here and I'll reply. I'm on Pacific time.
You might be able to export the config files and change the names in them and then re-import them. Or you might be able to run search-and-replace db queries but those might not work if serialization is involved. Or you could write a script to load everything in the db, change names, and write it back.
But, it's probably going to be easier to just do a migration. You can export your data to JSON using views (including custom fields, image paths, etc) and then migrate them in.
This shows how I structured it: https://github.com/TolstoyDotCom/migrate_json_test I should add a note that it expects the module to be in the modules/custom directory.
Drupal should avoid getting into political issues. That means removing the Ukraine banners, asking people what race they are, DEI initiatives, etc etc. This site and the software should be neutral.
You illustrate a problem that happens by not being neutral: now they have to come up with an explanation for why they'd support one issue over another.
There are plenty of other sites that are willing to take stands on issues.
I worked with that a while back but I forget the specifics. IIRC it wouldn't let you add something to the cart if it was out of stock and I had to add code to deal with things that could be backordered. I'm available for consulting if you don't figure it out.