Search the Community
Showing results for 'runtime'.
-
To be fair ProCache isn't really what one commonly understands by "static page". You cannot easily take what ProCache generates and e.g. publish it to GitHub pages or netlify. The cache is also generated on actual requests to a certain page. There's no command to "generate all pages". So you'll likely still need to run the full CMS on a webhosting doing php and having a mysql db. You just won't hit the php runtime/db for requests, which hit the cache. Therefore you can take a bunch more traffic on the same machine. For actual static pages (html/js/css as output) I really like eleventy. It's really flexible and if you like processwire for the authoring experience you could e.g. render a json document out of processwire, which you feed into eleventy as data source.
-
Yes. It is used extensively elsewhere in Availability.php and works fine. Config ($config->admin_site outputs ProcessWire 3.0.153 dev #1) No the host page is in site-web, which I think you are calling website_instance. No. It is in admin_instance, where the runtime markup field with Availability.php is.
-
Sorry - I forgot to say that I had tried various other things, including your suggestion. I've now done those things again (with $mailOwner rather than $mail - I'd forgotten that was predefined) and recorded the error in each case. These are reproduced below as a pair of lines - code followed by error: $mailOwner = $config->admin_site->wire('mail'); Could not get the right WireMail-Module (WireMailSmtp); found: WireMailTools [My debugging line] $mailOwner = $config->admin_site->mail->new(); require_once(./WireMailSmtpAdaptor.php): failed to open stream: No such file or directory in M:\laragon\www\BawdHall\site\assets\cache\FileCompiler\site\modules\WireMailSmtp\WireMailSmtp.module on line 290 The second of these seems to be calling the right module (WireMailSmtp) but can't find the file because $config is always for instance_A. This seems similar to the issue we had with Runtime Markup.
-
@MarkE, it is not necessary for me to know what and which forms you are using and how you have organized this. ? I only want to know where and how you call the WireMail object. Is it in the runtime markup field? Where and how do you call it?
-
In your terminology, lets say instance_B is "site" and instance_A is "site-web". Both sites sit under the same root, sharing the same wire/ (i.e. a multi-site installation). in _init.php (part of the template rendering) in instance_A: $config->admin_site = new ProcessWire($config->paths->root . 'site/'); I use $config->admin_site so that it can be accessed anywhere (assuming the current context is instance_A). Ah, that's a bit (!) more complicated. Instance_A accesses a runtime markup field in instance_B thus: $adminPage = $config->admin_site->pages->get("template=Availability, name=bawd-hall-availability"); $availabilityTable = $adminPage->runtime_markup_availability; This field renders php in the file Availability.php, which lives in instance_B ("site") Availability.php renders a form thus: $out .= $config->admin_site->files->render($currentPath . 'booking_form.php'); where $currentPath is $files->currentPath(); - i.e. booking_form is a sibling of Availability. (BTW, Availability.php checks the host name in the headers to determine whether it is being called as a second instance or not). booking_form's action is $_SERVER['REQUEST_URI'] (i.e. the original page, thus running Availability.php again). Availability.php tests (isset($_GET["submit"]) ) and if true processes the POST variables from the form. After validation, the mail is constructed from these variables with $mail = wireMail(); etc. For more background (if you really want ? ) you can see here, where @kongondo was extremely helpful getting the RuntimeMarkup Fields module working in a multi-instance environment.
-
That might be possible, if that were to be the only method. However, methods 1 and 2 require the data to be processed in the admin app as the front-end for those methods is not PW (and both these methods work well). In any case, I have been experimenting a bit with your suggestion, by putting my RM code on site 2 (since that is where the module is looking for it) and have been running into problems with selectors not working properly - will continue to investigate this. Excerpts of code are given below. The page is rendered using the markup regions strategy. _init.php is loaded first, the template is in Prices.php and the rendering is via _main.php. In _init.php $admin = new ProcessWire($config->paths->root . 'site/'); In _main.php <div id="wrapper"> <!-- content replaced by template --> </div> In Prices.php <div id="wrapper"> ....... <div id="availability"> <?php $adminPage = $admin->pages->get("template=Availability, name=bawd-hall-availability"); $availabilityTable = $adminPage->runtime_markup_availability; bd($adminPage, 'admin page'); bd($adminPage->availabilityColumns, 'admin page columns'); bd($availabilityTable, 'admin page runtime'); ?> <?= $adminPage->title ?> <?= $adminPage->runtime_markup_test ?> <?= $availabilityTable ?> </div> ---- </div> The bardumps illustrate that the other fields appear OK (as do $adminPage->title and $adminPage->runtime_markup_test {your test field - only works with the PHP placed in site-2/templates}). $availabilityTable (the original RM field) does not work even after placing a copy of the php in site-2/templates and modifying the $pages-> to invoke the site-1 instance, owing to the selector issue mentioned above, which I am investigating. EDIT: The selector issue is my code, I think. I got the field to render eventually in site 2 (minus a lot of css) with replicating all the PHP for the field in site-2/templates and modifying $pages etc. to pick up the right instances. Part of the issue seemed to be the need to explicitly set the $user in site 1, otherwise the field could not pick up the required data. I was assuming that since the API was all in PW that superuser in site-2 would allow API access in site-1; it doesn't! So, if we can get the RM module to pick up the code from the correct place, it may all work nicely.
-
I've now implemented a multi-site and multi-instance setup and it mostly works. For some reason, the 2nd site cannot access select options fields on pages on the 1st site - I changed these to page reference fields and that works. The only major problem so far is with runtime markup fields, which are pretty crucial to my app (great module btw @kongondo ? ). These don't seem to be accessible by the 2nd site at all - either directly or via $page->render(). Curiously, I had no problem accessing them from a non-PW site (on the same server).
-
CKEditor is stripping out your code since <healcode...> is not a valid HTML tag. You can tell it, via its ACF and/or Extra allowed content settings, to allow that tag, though am not sure even that would work. Another possibility is to use Hanna Code to insert that code at runtime. Parameters would have to be passed to it (the values you want the client to be able to change) so as to render them at runtime. The Hanna Code would be could like so in your body field: [[widget a=value1 b=value2 c=value3]] where a, b and c are the parameters whose value the editor can change. You can rename them to something your client would understand, e.g. type,partner,id, etc...However, do you still want your client to be editing the Hanna Code like that? Alternatively, you can create a field(s) to store your editable values and via the Hanna Code, tell it to read from that field(s). Or, if the values will always be coming from a set pool, you can save them beforehand to a pagefield. The client would then have select the applicable values in that pagefield. Hanna Code would then grab the values from that pagefield. Note that the above is not the Hanna Code code itself; just how to call it on your page. Anyway, please describe your scenario with a bit more specifics so that we can tailor our responses to that.
-
I've seen a couple of questions regarding namespaces and autoloading floating around the forum recently, so I decided to write a little tutorial. In general, I often see people getting confused when they try to wrap their head around namespaces, autoloading, Composer and the mapping of namespaces to directory structures all at once. In fact, those are very much independent, distinct concepts, and it is much easier to explain and understand them separately. So this guide is structured as follows: How namespaces work in PHP. How autoloading works in PHP. Conventions for mapping namespaces to directory structures: PSR-4. How autoloading works in Composer and ProcessWire's class loader. How to use the class loader in a ProcessWire module. Feel free to skip the sections you're already familiar with. Namespaces in PHP The purpose of namespaces in PHP is to avoid naming conflicts between classes, functions and constants, especially when you're using external libraries and frameworks. Nothing more. It's important to understand that this has nothing at all to do with autoloading, directory structures or file names. You can put namespaced stuff everywhere you want. You can even have multiple namespaces inside a single file (don't try this at home). Namespaces only exist to be able to use a generic name – for example, ProcessWire's Config class – multiple times in different contexts without getting a naming conflict. Without namespaces, I couldn't use any library that includes a Config class of it's own, because that name is already taken. With namespaces, you can have a distinction between the classes ProcessWire\Config and MoritzLost\Config. You can also use sub-namespaces to further segregate your code into logical groups. For example, I can have two classes MoritzLost\Frontend\Config and MoritzLost\Backend\Config– a class name only needs to be unique within it's namespace. You can declare the namespace for a PHP file using the namespace statement at the top: // file-one.php <?php namespace ProcessWire; // file-two.php <?php namespace MoritzLost\Frontend; This way, all classes, methods and constants defined inside this file are placed in that namespace. All ProcessWire classes live in the ProcessWire namespace. Now to use one of those classes – for example, to instantiate it – you have a couple of options. You can either use it's fully qualified class name or import it into the current namespace. Also, if you are inside a namespaced file, any reference to a class is relative to that namespace. Unless it starts with a backward slash, in this case it's relative to the global namespace. So all of those examples are equivalent: // example-one.php <?php namespace ProcessWire; $page = new Page(); // example-two.php <?php use ProcessWire\Page; $page = new Page(); // example-three.php <?php $page = new ProcessWire\Page(); // example-four.php <?php namespace MoritzLost\Somewhere\Over\The\Rainbow; $page = new \ProcessWire\Page(); The use statement in the second example can be read like this: “Inside this file, all references to Page refer to the class \ProcessWire\Page” How autoloading works Every PHP program starts with one entry file – for ProcessWire, that's usually it's index.php. But you don't want to keep all your code in one file, that would get out of hand quickly. Once you start to split your code into several individual files however, you have to take care of manually including them with require or include calls. That becomes very tedious as well. The purpose of autoloading is to be able to add new code in new files without having to import them manually. This, again, has nothing to do with namespaces, not even something with file locations. Autoloading is a pretty simple concept: If you try to use a class that hasn't been loaded yet, PHP calls upon it's registered autoloaders as a last-ditch attempt to load them before throwing an exception. Let's look at a simple example: // classes.php <?php class A { /** class stuff */ } class B { /** class stuff */ } // index.php <?php spl_autoload_register(function ($class) { include_once 'classes.php'; }); new A(); new B(); This is a complete and functional autoloader. If you don't believe me, go ahead and save those two files (classes.php and index.php) and run the index.php with php -f index.php. Then comment out the include_once call and run it again, then you'll get an error that class A was not found. Now here's what happens when index.php is executed (with the autoloader active): Our anonymous function is added to the autoload queue through spl_autoload_register. PHP tries to instantiate class A, but can't because it's not loaded yet. If there was no autoloader registered, the program would die with a fatal error at this point. But since there is an autoloader ... The autoloader is called. Our autoloader includes classes.php with the class definition. That was a close one! Since the class has been loaded, execution goes back to the index.php which can now proceed to instantiate A and B. If the class was still not loaded at this point, PHP would go back to the original plan and die. One thing to note is that the autoloader will only be called once in this example. That's because both A and B are in the same file and that file is included during the first call to the autoloader. Autoloading works on files, not on classes! The important takeaway is that PHP doesn't know if the autoloader knows where to find the class it asks for or, if there are multiple autoloader, which one can load it. PHP just calls each registered autoloader in turn and checks if the class has been loaded after each one. If the class still isn't loaded after the last autoloader is done, it's error time. What the autoloader actually does is pretty much wild wild west as well. It takes the name of the class PHP is trying to load as an argument, but it doesn't have to do anything with it. Our autoloader ignores it entirely. Instead, it just includes classes.php and says to itself “My job here is done”. If class A was in another file, it wouldn't have worked. This process has two main advantages: Since autoloaders are only called on-demand to load classes just in time, we only include the files we actually need. If in the example above class A and B are not used in some scenarios, the classes.php will not be included, which will result in better performance for larger projects (though this isn't as cut and dry, since autoloading has it's own overhead, so if you load most classes anyway during a single request, it will actually be less efficient). If the autoloader is smart enough to somehow map class names to the files they're located in, we can just let the autoloader handle including the classes we need, without having to worry about jamming include statements everywhere. That brings us to ... PSR-4, namespaces and directory structures As you see, namespaces and autoloading are both pretty limited concepts. And they aren't inherently linked to each other. You can namespace your classes without ever adding an autoloader, and you can autoload classes that are all in the same namespace. But they become useful when you put them together. At the core of all that autoloading talk is a simple idea: By putting classes in files named after their class names, and putting those files in directory hierarchies based on the namespace hierarchy, the autoloader can efficiently find and load those files based on the namespace. All it needs is a list of root namespaces with their corresponding directories. The exact way class names and namespaces are mapped to directory structures and file names is purely conventional. The accepted convention for this is PSR-4. This is a super simple standard which basically just sums up the ideas above: A base namespace is mapped to a specific directory in the file system. When the autoloader is asked to load a class in that namespace (or a sub-namespace of it), it starts looking in that folder. This "base" namespace may include multiple parts – for example, I could use MoritzLost\MyAwesomeLibrary as a base and map that to my source directory. PSR-4 calls this a "namespace prefix". Each sub-namespace corresponds to a sub-directory. So by looking at the namespace, you can follow subdirectories to the location where you expect to find the class file. Finally, the class name is mapped directly to the file name. So MyCoolClass needs to be put inside MyCoolClass.php. This all sounds simple and straightforward - and it absolutely is! It's only once you mash everything together, mix up language features, accepted conventions and proprietary implementations like Composer on top that it becomes hard to grasp in one go. Composer and ProcessWire's class loader Now all that's left is to talk about how Composer and ProcessWire provide autoloading. Composer, of course, is primarily a tool for dependency management. But because most libraries use namespaces and most developers want to have the libraries they're using autoloaded, those topics become a prerequisite to understanding what Composer does in this regard. Composer can use different autoloading mechanisms; for example, you can just give it a static list of files to include for every request, or use the older PSR-0 standard. But most modern libraries use PSR-4 to autoload classes. So all Composer needs to function is a mapping of namespace prefixes to directories. Each library maintains this mapping for it's PSR-4-structured classes through the autoload information in their composer.json. You can do this for your own site to: Just include the autoload information as shown in the documentation and point it to the directory of your class files. Composer collects all that information and uses it to generate a custom file at vendor/autoload.php — that's the one you need to include somewhere whenever you set up Composer in one of your projects. Bells and whistles aside, this file just registers an autoloader function that will use all the information collected from your own and your included libraries' composer.json to locate and include class files on demand. You can read more about how to optimize Composer's autoloader for production usage here. If you want to read up on how to set up Composer for your own sites, read my ProcessWire + Composer integration guide instead. And finally, what does ProcessWire do to handle all this? Turns out, ProcessWire has it's own autoloader implementation that is more or less PSR-4 compliant. You can access it as an API variable ($classLoader or wire('classLoader'), depending on context). Instead of using a static configuration file like Composer, the namespace -> directory mapping is added during the runtime by calling $classLoader->addNamespace. As you would expect, this function accepts a namespace and a directory path. You can use this to register your own custom namespaces. Alternatively, if you have site-specific classes within the ProcessWire namespace, you can just add their location to the class loader using the same method: $classLoader->addNamespace('ProcessWire', '/path/to/your/classes/'). Utilizing custom namespaces and autoloading in ProcessWire modules Now as a final remark, I wanted to give an example of how to use custom namespaces and the class loader in your own modules. I'll use my TrelloWire module as an example: Decide what namespace you're going to use. The main module file should live in the ProcessWire namespace, but if you have other classes in your module, they can and should use a custom namespace to avoid collisions with other modules. TrelloWire uses ProcessWire\TrelloWire, but you can also use something outside the ProcessWire namespace. You need to make sure to add the namespace to the class loader as early as possible. If either you or a user of your module tries to instantiate one of your custom classes before that, it will fail. Good places to start are the constructor of your main module file, or their init or ready methods. Here's a complete example. The module uses only one custom namespaced class: ProcessWire\TrelloWire\TrelloWireApi, located in the src/ directory of the module. But with this setup, I can add more classes whenever I need without having to modify anything else. /** * The constructor registers the TrelloWire namespace used by this module. */ public function __construct() { $namespace = 'ProcessWire\\TrelloWire'; $classLoader = $this->wire('classLoader'); if (!$classLoader->hasNamespace($namespace)) { $srcPath = $this->wire('config')->paths->get($this) . 'src/'; $classLoader->addNamespace($namespace, $srcPath); } } Source Thanks for making it through to the very end! I gotta learn to keep those things short. Anyway, I hope this clears up some questions about namespaces and autoloading. Let me know if I got something wrong, and feel free to add your own tips and tricks!
- 5 replies
-
- 37
-
-
-
- autoload
- namespaces
-
(and 2 more)
Tagged with:
-
Most of us know and use site/config-dev.php file. If present, it is used instead of site/config.php, so it is easy to set database connection and debug mode for local development, not touching the production config. It is also very useful when working with git. You can simply ignore it in the .gitignore file, so local settings won’t end up in the repo. But sometimes you need to add code to site/ready.php or site/init.php just for the dev environment. For example, to add ryan’s super cool on demand images mirrorer. I can’t live without it when working with big sites, which have more assets then I want to download to my desktop. It would be great if there was something like site/ready-dev.php for this. Not out-of-the-box, but it’s pretty easy to achieve. Unlike site/config-dev.php, site/ready.php is not hardcoded. It’s name is set with a special config setting: // wire/config.php $config->statusFiles = array( 'boot' => '', 'initBefore' => '', 'init' => 'init.php', 'readyBefore' => '', 'ready' => 'ready.php', 'readySite' => '', 'readyAdmin' => '', 'render' => '', 'download' => '', 'finished' => 'finished.php', 'failed' => '', ); As you can see, we can not only define, which files are loaded on init, ready and finished runtime states, but probably even add more if we need to. So we override this setting in site/config-dev.php like this: // site/config-dev.php // Change ready.php to ready-dev.php $temp = $config->statusFiles; $temp['ready'] = 'ready-dev.php'; $config->statusFiles = $temp; For some reason we can’t just do $config->statusFiles['ready'] = 'ready-dev.php'; and have to override the whole array. Maybe you PHP gurus can explain this in the comments. Now we can create the site/ready-dev.php file and place all the dev-only code there. Important thing is to include the main site/ready.php. // site/ready-dev.php include 'ready.php'; // DEV HOOK TO MIRROR ASSETS ON DEMAND $wire->addHookAfter('Pagefile::url, Pagefile::filename', function($event) { $config = $event->wire('config'); $file = $event->return; if($event->method == 'url') { // convert url to disk path $file = $config->paths->root . substr($file, strlen($config->urls->root)); } if(!file_exists($file)) { // download file from source if it doesn't exist here $src = 'https://mysite.com/site/assets/files/'; $url = str_replace($config->paths->files, $src, $file); $http = new WireHttp(); try { $http->download($url, $file); } catch (\Exception $e) { bd($file, "Missing file"); } } }); Do not forget to replace "mysite.com" if you’re copypasting this)) Now, add the newly created file to the `.gitignore` and we’re done. # .gitignore # Ignore dev files site/config-dev.php site/ready-dev.php Thanks for reading!
-
Hi @csaggo.com It seems like there is no url field for opengraph tags. At the moment, the og url is generated dynamically based on the page's httpUrl, so you cannot change it at runtime. If the module would use the canonical url at render time, it should work. Can you open an issue on GitHub? ? Cheers
-
Module Module: RuntimeMarkup Fieldtype & Inputfield
Robin S replied to kongondo's topic in Modules/Plugins
@psy, in theory you don't need any sort of special module to put runtime markup into a field. Instead just add a Markup field to the form and then hook before the form is rendered to put whatever markup you want into it. Having said that, there are some oddities with Markup fields in FormBuilder. I raised some questions that Ryan answered here: But I still had some other problems (I can't remember exactly what they were now) that I ended up resolving by making a clone of the core InputfieldMarkup module with some minor changes. I've attached the resulting InputfieldCustomMarkup module. If you install this module and enable it for use in FormBuilder then you can use it the same as a Markup field but without the snags. InputfieldCustomMarkup.zip -
This one is a little bit special... I want to set an Inputfield setting at runtime via code. This works great for changing Inputfield labels or description and having everything under GIT. I'm using ProcessPageEdit::buildForm to modify the Inputfield before it gets rendered. The problem is that this technique does not work in several situations when AJAX is involved. For example I can not set InputfieldFile settings like max filesize or another example is the noLang = 1 setting that hides the multilanguage field descriptions of the Inputfield and shows only one inputfield. When using the buildForm hook, both settings modifications have no effect on newly uploaded files. That means the max filesize will never get checked and the setting noLang = 1 does also only work for existing files. When I upload a file it shows both language inputfields for the description and shows one just after saving the page (because then the buildForm hook knows about the file where to set noLang = 1). Any ideas where I could hook into to modify the settings stored in the database as early as possible? Do you understand my problem / question? ?
-
RockLESS - PHP LESS parsing helper module.
gornycreative replied to bernhard's topic in Modules/Plugins
It looks like this module could allow for the introduction of field content prior to compiling - is that accurate? Is there anything special that needs to be done to prioritize it at runtime before ProCache? I'm looking to be able to push field values from SettingsFactory into the LESS file preproc. -
I'm looking for an inputfield module that might allow the entry and evaluation of a conditional expression (following php syntax). My use case is an admin function for writing pro-forma emails/letters where some components of the pro-forma are dependent on conditions determined by an admin user. The pro-forma is then cloned for use and the relevant components are included depending on the runtime value of the conditions. The conditions usually include hanna codes as the items to be compared. This is implemented in a parent-child structure, where the main mail body is in the parent and each child then has a condition (textarea) field and a body textarea field for the optional text relating to that condition. As an interim solution on my dev machine, I am just using eval() to evaluate the conditions, but I really don't want to use this in the live environment. My idea is to use an approach similar to that for hanna codes to store the php and render it. This would be (somehow) wrapped in a new inputfield module (extending InputfieldTextarea?) with an evaluate() method that would return true or false as appropriate. It would be placed inside a try...except structure to catch syntax errors etc. It seemed to me that this might be quite a useful utility module and that someone might have developed something similar, but I can't find anything. Does anyone have any pointers, or will I need to start from scratch? If the latter, then I'd appreciate some help along the way as I am a bit of a novice in these matters.
-
Can you try a few different things for me: 1) Empty the PW trash 2) Do a field export (from the main fields list page) 3) Install the core ProcessPageClone module and click the new "COPY" page list action button If those all succeed without error, I'll revisit, but otherwise I'll need to update Tracy to be PW 3.x only so that it avoids the FileCompiler completely. That would solve the Console and APIExplorer panel error. The PW Info panel error is due to the Versions List feature trying to check if the system allows runtime modification of max_execution_time so I could conditionally remove or deal with that, but I'd like to make sure those other core PW things work on that host first. It's also weird how one.com seems to disable set_time_limit / max_execution_time some other way - ie not in disable_functions - make it much harder to detect.
-
Show data in locked custom field – 'Locked' field not showing
adrian replied to jonatan's topic in General Support
@jonatan - looks like you used http://modules.processwire.com/modules/fieldtype-runtime-markup/ rather than https://github.com/Toutouwai/FieldtypeRuntimeOnly but they both do much the same thing. Anyway, glad you got it working and thanks for chiming on that Github request - that visibility stuff definitely needs an overall.- 8 replies
-
- 2
-
-
- locked field
- collapsenolocked
-
(and 3 more)
Tagged with:
-
Show data in locked custom field – 'Locked' field not showing
jonatan replied to jonatan's topic in General Support
@adrian Thank you so much!!!!! ?☀️??? And thank you for such an amazing response time! - Done! - Worked like a charm!!!! Strongly back on the I-love-processwire-and-think-it-is-so-amazing-train! ??? ✅ Solution: Installed RuntimeMarkup Fieldtype module, made a new field with that type: DETAILS > : Edited runtime php file: site/templates/instagramaccountid.php: <?php namespace ProcessWire; $instagram = $modules->get("InstagramBasicDisplayApi"); $data = array('username'=>''); $account = $instagram->getUserAccount($data["username"]); $username = isset($account["username"]) ? $account["username"] : ""; $at_instausername = "@" . $username; echo$at_instausername; ?> Result: One piece of beautifully dynamically called data shown in as sweet plain text in the defined field in the template: Could be really cool though if this kind of custom field would maybe become integrated in the core? As well as an "locked / unlocked" option for all fields? Thanks again! All the best, Jonatan- 8 replies
-
- 1
-
-
- locked field
- collapsenolocked
-
(and 3 more)
Tagged with:
-
Show data in locked custom field – 'Locked' field not showing
adrian replied to jonatan's topic in General Support
Hi @jonatan and welcome to the forums. The "Open when populated + Closed when blank + Locked (not editable)" is the closest to what you are looking for. You might actually want to add your support to this request: https://github.com/processwire/processwire-requests/issues/346 Back to your original question, I haven't used Mystique, so not sure what its limitations might be on this front, but I wonder if you can make use of https://github.com/Toutouwai/FieldtypeRuntimeOnly to add the instagram data in a runtime field.- 8 replies
-
- 1
-
-
- locked field
- collapsenolocked
-
(and 3 more)
Tagged with:
-
Here's what the code actually says. In the Page class, we have these constants: const statusOn = 1; // base status for all pages const statusLocked = 4; // page locked for changes. Not enforced by the core, but checked by Process modules. const statusSystemID = 8; // page is for the system and may not be deleted or have it's id changed (everything else, okay) const statusSystem = 16; // page is for the system and may not be deleted or have it's id, name, template or parent changed const statusHidden = 1024; // page is excluded selector methods like $pages->find() and $page->children() unless status is specified, like "status&1 const statusUnpublished = 2048; // page is not published and is not renderable. const statusTrash = 8192; // page is in the trash const statusDeleted = 16384; // page is deleted (runtime only) const statusSystemOverride = 32768; // page is in a state where system flags may be overridden const statusCorrupted = 131072; // page was corrupted at runtime and is NOT saveable const statusMax = 9999999; // number to use for max status comparisons, runtime only These are all the possible values for status. However, status is a bitmask, so it can have more than one value at a time. However, the status levels are ordered from most visible to least visible, so selectors like "include=all" actually translate to "status<9999999" and "include=hidden" translates to "status<2048". Next is the PageFinder class, which is where the "include=all" and "include=hidden" are defined, among other things. There is also an internal mode in PageFinder called "findOne" that is an option that the $pages->get() function uses. The findOne option is not one that you would use on your own, it's only an internal designation. The findOne option states that: 1) return only the first match; 2) skip over any access control checks; 3) limit to pages with status less than unpublished (unless another status is specified or include=all is specified). There is also the "findAll" option, which is what is assumed when you specify "include=all". That one states that 1) the status must be less than statusMax (meaning, it can include any available status); and 2) skip over any access control checks. Again, you don't need to remember what findOne and findAll are, since you won't ever use them. But I mention them just to explain how selectors translate in the system. $pages->get() or $pages->find() are not going to include pages in the trash unless you specify an "include=all" or one of the high status levels. $pages->get() is different from $pages->find() in these respects: $pages->get() only ever returns 1 page. $pages->get() bypasses access control and will include hidden pages. You don't need to specify "include=all" to $pages->get(); unless you want to retrieve an unpublished page or a page from the trash. If there are times when you want a $pages->get() that performs exactly like $pages->find(), except only returning one page, you can do this: $page = $pages->find("your selector, limit=1")->first(); if($page) { // you got it } Or you can do this: $page = $pages->get("your selector"); if($page->viewable() && !$page->isHidden()) { // you got it } I almost never need to do any this though (or necessarily even remember it). $pages->find() behaves the way I usually want it to, including things that aren't viewable and things that don't belong in lists and navigation. And $pages->get() behaves the way I usually want it to, returning the single specific thing that I asked for.
- 74 replies
-
- 1
-
-
- fields
- alter table
-
(and 1 more)
Tagged with:
-
solved new user role can't publish...must be missing something.
ryan replied to evan's topic in Getting Started
Once a system has a page-publish permission, it changes the behavior of page-edit as well. From that point forward, a user with page-edit permission must also have page-publish permission in order to edit a published page. The "who can access this page?" screen is telling you as much as it can about individual roles, but it's not telling you anything about combinations of roles. If a page is published, and there is a role that has page-edit, but not page-publish, then it's going to say it's not editable by that role. And that's true, because if you take that role by itself, the page isn't editable. Likewise, if you've got a role with page-publish, but not page-edit, a user with that role by itself can't edit anything. So this information section can't tell you anything about what might be the results of combining roles. Since that is determined at runtime, specific to a user, the only way you can tell is to try it with an actual user. Before I experiment too much here, I should check if you are testing by logging in as the user, or just by looking at what this info screen says? Stacked permissions should still work for the actual user at runtime. Though let me know if you find they aren't? These settings are cached with the template. So it may say one thing here based on that cached state, but again the runtime result of the actual permissions for a user should be consistent with the user's actual permissions. Let me know if you find it is not, as that would be a bug. I agree this must look confusing. Those checkboxes are getting populated with the cached value from the template. It sounds like what I need to do here is prevent "disabled" attribute checkboxes from having a checked value.- 19 replies
-
Hi, I'm facing a strange issue with priority values being output with colon (0,5) instead of period (0.5) which I guess is wrong. On some servers PW issues a warning to translate the locale setting: I have a site with a single language (German) and language support enabled to have the backend translated to German. So I translated the locale setting to de_DE.UTF-8 But now sitemap items that are generated through a hook will output 0,5 (which is correct for German locale). Other pages in the sitemap output correctly like 0.5. I have automatic sitemap generation disabled because I update it manually whenever some data changes that comes from an external XML and is converted to virtual pages on runtime that do not get saved to the DB. My code for adding those virtual page entries to the sitemap public function updateXmlSitemap() { $config = $this->wire('modules')->getConfig('SeoMaestro'); $items = $this->stellenangeboteXMLToPageArray(); // results in array of virtual pages $entries = array(); foreach ($items as $item) { $entry = (new SitemapItem()) ->set('loc', $config['baseUrl'] . $item->url) ->set('lastmod', date('c'), time()) ->set('priority', (float) 0.7) // casting explicitly to float doesn't help ->set('changefreq', 'daily'); $entries[] = $entry; } $this->addHookAfter('SeoMaestro::sitemapItems', function (HookEvent $event) use ($entries) { $event->return = array_merge($event->return, $entries); // add my entries to the sitemap }); $manager = new SitemapManager($config); try { $sitemap = $manager->generate($this->wire('config')->paths->root . $config['sitemapPath']); if (!$sitemap) { $this->wire('log')->save(strtolower($this->className), 'Could not generate sitemap'); } else { $this->wire('log')->save(strtolower($this->className), 'sitemap generated'); } } catch (\Throwable $th) { $this->wire('log')->save($this->className, 'Error generating sitemap' . $th->getMessage()); } } I don't know why the items added to the sitemap through my hook produce different output (0,5) from the items that are generated by the module (0.5). Could you make sure in the module code that the priority value is always being rendered as a float value, no matter what locale setting? That would be awesome.
-
Are you saying that there is something wrong with my proposed solution, or are you saying that there is something else wrong with the existing field type? I think either your post went over my head, or you may be misunderstanding my proposed solution. I think the solution is not actually that complicated. ProcessWire natively works with timestamps at runtime, which are always UTC-based. If I save something as a certain timestamp and then my PW/server/php time changes for any reason, I should be able to expect that the timestamp I get back from the field remains the same as the one I put into it. This is how it would work if the field stored a UTC string instead of a local string. Currently, what I get back is effectively a corrupted/meaningless value. I cannot change my $config->timezone once I've initially set it. What timezone the user sees or inputs a date in on the site's front end is a separate issue and is up to the programmer to determine and make clear to the user and convert to/from as necessary. But the programmer should be able to trust the timestamp they are working with when they save and retrieve it from the database. What does this mean?
-
Thanks a lot, dragan. You’re right, I should have specified the "where" a bit more – in this case I meant the page itself in edit mode as in your case 2. Anyway, I checked out the runtime markup field and it does exactly what I was looking for with very little effort. Perfect solution!
-
Where exactly? "on the admin page" is a bit vague... In the top menu? In a particular page when in page-edit mode? In the page-tree? elsewhere? For use-case 2, you could try https://modules.processwire.com/modules/fieldtype-runtime-markup/ Another very useful module is https://modules.processwire.com/modules/page-field-info/ - especially if you need to provide contextual information based on your selection (checkboxes, radio buttons, page fields, options...).