Jump to content

MarkE

Members
  • Posts

    931
  • Joined

  • Last visited

  • Days Won

    10

Everything posted by MarkE

  1. I'm sure I read something about this recently, but I can't find it ... (is there a way of bookmarking forum posts?) I'm trying to move my page hooks out of ready.php into Page Classes, which I think are tremendous and use extensively. But what is the best way to do it? ready() doesn't trigger automatically. I tried public function ___loaded() { $this->ready(); } (thanks to @Robin S) and this does trigger it. However, if I do, say public function ready() { $this->addHookAfter('Pages::save', $this, 'afterSave'); $this->addHookBefore('Pages::save', $this, 'beforeSave'); } then the hook methods get triggered by any page save, not just of instances of this class. I solved this by adding a breakout at the start of the method: protected function beforeSave(HookEvent $event) { $p = $event->arguments(0); if ($this != $p) return; ...... but this seems a bit inelegant. Any better ideas?
  2. I quite agree - I use custom page classes for practically every template. One question, though not entirely specific to RM: how do you deal with functions in init.php that are used by multiple classes? In my dbMigrate module in the 'parallel universe' of UI development ? I use some custom methods and functions that are in my DefaultPage.php and init.php because I use them more widely than just in the module. Of course, they can be readily found with the IDE (although it is an issue with packaging the module), but your comment about init.php made me think that I am missing a trick.
  3. I've reworked this slightly, based on the helpful suggestion of @Kiwi Chris. The individual migration items are now entered in a repeater field and so the sequence can take into account any dependencies. Each item can be either 'new', 'changed' or 'removed'. When a migration is uninstalled, the sequence is automatically reversed and 'new' items are changed to 'removed' and vice versa. Example below shows a migration testing a page reference dependency (it works!). This is the appearance in the source database (pre-export): If you click 'Preview' you see what changes are proposed to export (see below). This feature also operates to review (in the target database) what changes will happen on install or uninstall - or, if the install has failed, what changes remain (either by modifying the migration or applying a manual fix if all else fails). Export is shown as 'no object' in the above, because the migration has not yet been exported. I think this all seems to work as designed, but am grateful for any futher thoughts on the design. I will now work on tidying the code a bit and doing a bit of documentation. There may still be a few bugs. There are some at the moment that I can't pin down but they are cosmetic rather than fundamental - I think I may need some help from those who understand the inner workings of PW better ( @adrian ?). Promises of help will encourage me to get on and release some code ? Meanwhile, I am happy to answer any further questions.
  4. Originally I was using select fields and lookups, but as the objects are not necessarily in the current database (and it depends on whether you are in a source or target environment), this made things troublesome and inconsistent, so I reverted to plain text. Your general suggestion of item by item ordering is interesting and I will consider that, thanks.
  5. The sequence is: remove (pages > templates > fields); add/change (fields > templates > pages). Within each type (e.g. pages), the sequence is in the order listed in the entry. I think this caters for most situations - e.g. a new page select field followed by a changed template, followed by pages in the right dependency order - but it may not cover everything (particularly making the uninstall happen correctly too). In mitigation, unsuccessful migrations can be reviewed via the previews (see example below) and more complex migrations can be broken into smaller migrations to better handle dependencies (I had thought of providing dependency linkage between migrations, but at the moment I think that adds too much complexity). Pic below shows an example preview with one 're-purposed' field and one new one - this can be reviewed before implementation and also afterwards if the install was not complete. I will take a closer look at that to see if it helps any. Quite! IMHO it is the main area where PW is lacking, particularly given that it is such a good tool for heavy-duty apps as well as just websites - a point that has been well made by @Kiwi Chris. I was surprised and a bit disappointed by how little attention this has had in terms of core modules. Not sure if I need to loop round like this (and also as @adrian indicates). Further testing may indicate that it is necessary. Well, they could but the tools are not complete (e.g. fields does not handle select options and pages does not exist except 'in development'); for anything but the simplest migration, you have to do several manual steps in the right order (coupled with working out what didn't implement correctly) - this can be a real pain if you are testing several times then needing to implement on the live version and can run a real risk of making mistakes when doing it for the n'th time; there is no documentation of what you have done; there is no ability to uninstall other than to repeat the manual steps correctly in reverse order. For very simple migrations (e.g. one changed template), then it is a feasible approach. I wanted something that installs fast and consistently every time - particularly when it is interdependent with changed code and I want to minimise downtime. Also, it is not just a case of 'not wanting to learn'. Code-based migrations need familiarity through frequent use and work best in the context of code-based app development where the same comment applies. I'll do a bit more testing and try and cover some of the points made above, then do this. Meanwhile, the main task is to refactor and restructure the code which is a truly horrible lot of spaghetti at the moment. If this is to go any further, I will then need some help, particularly with turning it into a more 'professional' module, as my PHP (particularly OOP) skills are limited and I have never previously done anything beyond simple Process modules .
  6. Well, yes and no. Two migration modules already exist in ProcessWire, but neither suited my needs: “Migrations” by @LostKobrakai seems effective but quite onerous to use and has been deprecated in favour of “RockMigrations” RockMigrations by @bernhard is simpler and has a nice declarative method: migrate(). However, it is ideally suited to “headless” development, where the API is used in preference to the Admin UI. This is great for professional PW developers, but for occasional developers like me, it is much easier to use the UI rather than just the API. In addition there @adrian's ProcessMigrator which is designed for migrating page trees. Concept I wanted something to achieve the following: To allow development to take place (in a separate environment on a copy of the live database, or on a test database with the same structure) using the Admin UI. When finished, to permit a declarative approach to defining the migration and implementing it (again in the UI). To allow testing of the migration in a test environment on a copy of the live database. To allow roll-back of a migration if installation causes problems (ideally while testing rather than after implementation!). To provide a record of changes applied. Although not originally intended, the module I developed also allows the selective reversion of parts of the database by exporting migration data from a backup copy. Also, if changes are made directly on the live system (presumably simple, low-risk mods – although not best practice), it allows reverse migration to the development system in a similar fashion. I should emphasise that what I have built is more of a 'proof of concept' than a fully-fledged module. The code is pretty hacky and uses some stuff outside of the module itself. Lots of validation is missing. However, I have used it successfully in a number of small tests and a medium-sized live migration. If there is sufficient interest, I will tidy the code and make it available, but it would still need input from better coders and PW-savants than me to make it into something more widely usable. EDIT: Please note that the module has moved on a bit from this original post - the design has changed somewhat to make it more robust and flexible and additional features have been added. Please see the help file for full details. I still consider it to be at alpha stage, however, so use with care - test before making migrations and always take backups first. Design The module has the following principal components: A PW module “ProcessMigrateData”, bundled with a bootstrap migration in the same ProcessMigrateData folder, to be placed in the site/modules folder; A Page Class “MigrationPage” to be placed in the site/classes folder; Php files migrationActions.php and migrationControl.php to be placed in the site/templates/RuntimeMarkup folder (and migrationActions.js to be placed in site/templates/RuntimeMarkup/scripts). There are also a methods which need to be put in class DefaultPage and a functions in the init.php file. The module requires the FieldtypeRuntimeMarkup module. Migration definitions are held in .json files in the ProcessMigrateData/migrations/{migration name} folder (I might move this). This folder contains up to 2 sub-folders - “new” and “old” which each contain a file called a migration.json file, which defines the scope of the migration in terms of fields, templates and pages, and also one or more of fields.json, templates.json, pages.json and remove.json. The first 3 of these files contain the field, template and file definitions within the migration scope and the remove.json file simply lists fields, templates and pages to be removed. These migration files are mirrored by pages of template “Migration” under a parent /migrations/ of template “Migrations”. The mirroring happens in two ways: If a new migration is created in the module (from the Setup -> DB Migration menu), then initially no json files exist. The json files are created, after the scope of the migration is defined on the page, by running “Export Data” from the eponymous button. If json files exist, but there is no matching migration page, then the latter is created by the module on accessing the DB Migration admin page. In this case, we are in the “target” database so there is no “Export Data” button, but instead “Install” and/or “Uninstall” buttons. Migrations therefore either view the current environment as a “source” (type 1) or a “target” (type 2). Installation The module creates templates called Migration and Migrations and a page below the root named ‘migrations’. Open the admin page “Setup -> DB Migration” to create a migration. One (“bootstrap” is already installed) and cannot be modified. The pic below illustrates the DB Migrations page in the source environment. The status of a migration (as a source page) can be ‘pending’ or ‘exported’. ‘Pending’ means either that the migration data have not yet been exported or that the current export files differ from the source database. On opening this page in the target environment, the individual Migration pages (type 2) are created from the definitions in their respective /new/migration.json file. The pic below illustrates the DB Migrations page in the target environment. In a target environment, a migration status can usually be ‘indeterminate’, ‘installed’ or ‘uninstalled’. ‘Indeterminate’ means either that the migration has not yet been installed (so no ‘old’ files containing the uninstall definition exist yet) or that the current state matches neither the ‘new’ or the ‘old’ state. ‘Installed’ means that the current state matches the ‘new’ definition and ‘uninstalled’ means that it matches the ‘old’ definition (i.e. it has been actively uninstalled rather than not yet installed). When carrying out development work, you keep a note of what fields, templates and pages you have added, changed or removed. The module does not track this – it is a declarative approach, not a macro recorder. Also, it does not handle other components such as Hanna codes and Formbuilder forms. These come equipped with their own export/import functions. You can update a migration page as you go along, rather than keep a separate note of changed components. The migration page also allows you to document the migration and add any number of “snippets”. These snippets do not do anything, but can be a convenient place to store (for example) Hanna code exports for pasting into the target environment and help to make the page a comprehensive record of the migration. See example below: Note that migration pages just define the scope of the migration. It is entirely feasible for other parts of the dev database to be changed which are outside this scope and which will therefore not be migrated. After sync'ing code files to the target environment, the new migration will be listed on the setup page. On the migration page, in the target environment, there are “preview” buttons to see what changes will be implemented. The migration can then be 'installed'. See example of the migration page in ‘installation’ mode below: That's the gist of it, but inevitably there are complications. Happy to discuss and share further if there is interest in this.
  7. Well, no different, in a way, as it uses all that code. It just provides a UI to pull it all together and automate all the export/import in the right order. More later...
  8. Easier said than done. I'll see what I can do. I can see that if you are using RockMigrations from the start, then the issue does not arise as all fieldnames will be lowercase. My situation is a bit different as I only use PW occasionally, and find the Admin UI, rather than a 'headless' approach, works better for me. However, I find migrations a pain. Consequently, I have started working on a UI interface module for your module, which is coming along quite nicely and which I will provide more details on once I have it in a reasonable state. It generates json files from the database which are then used as input to the migration, so it uses the existing fieldnames, which may not be lower case. Having run into this, as well as a few other problems with using RockMigrations in this way, I have switched to using the core code directly as I appreciate that RockMigrations was not designed with this in mind. For fields, I am using a modified copy of ProcessFieldExportImport::processImport() (although that has problems with options fields), for templates, I call ProcessTemplatesExportImport::setImportData() and am just using the standard API for pages. However, there are attractions in using the RockMigrations methods so I may return to this once all the UI stuff is working, particularly if you think it is a good idea.
  9. Hi @bernhard, just a simple question: Why in createField() - line 466 of RockMigrations.module, do you have if(strtolower($name) !== $name) throw new WireException("Fieldname must be lowercase!"); I thought the rules were: upper and lowercase ASCII letters, digits and underscore. Would it not be better to use $sanitizer->fieldName() or am I missing something?
  10. $this->rm()->migrate([ 'pages' => [ 'home' => [ 'title' => 'system', 'parent' => 0, 'template' => 'home' ] ] ]); This just changes the title, for simplicity. The original changed other fields and was part of a bigger migration. But running just this should highlight the issue.
  11. in json format - before conversion into array and used in migrate(['pages' => ...]) : "home": { "template": "home", "parent": 0, "status": 9, "title": "System", "summary": "This is the \"home\" page for the admin site - i.e. all other pages are children of it. It therefore holds some system-wide settings. You can change the title to suit any system-wide branding.\r\nSome of the fields (operator, websites and financial year) are duplicated on Property pages. The currently active property is shown in the top menu - if there is one and it has these duplicated fields filled on its page then they will take precedence, otherwise the values on this page will be used. The values on this page will be used if no property is active.\r\nYou can also manage image pages and management roles from this page.\r\nOne of the image pages is called \"letterhead\". Any images on that page will be used in letters/emails if no property is active or if the active property has no images on its letterhead page. You may create other image pages to hold images that you might want to use system-wide.", "operator": "/contacts/xyz/", "webmaster": "/contacts/xyz/", "website": "", "website2": "", "image_single": {}, "taxYear": null, "dayOfMonth": "", "month": {}, "taxYear_END": null }, but any home page would throw the error, I think.
  12. Actually it found me ? My code could probably be improved, but here is wahat I did (from around line 1190) // if the page is the home page then we need to avoid referring to its parent when saving if ($parent === 0) { $parent = ''; unset($data['parent']); } else { // make sure parent is a page and not a selector $parent = $this->pages->get((string)$parent); } // get page if it exists if ($parent and $parent->id) { $selector = [ 'name' => $name, 'template' => $template, 'parent' => $parent, ]; } else { $selector = [ 'name' => $name, 'template' => $template ]; } $page = $this->pages->get($selector); Hope that helps.
  13. One (rather trivial) issue: if you attempt to migrate the home page, then PW throws an error because the parent is invalid. It seems to be necessary to unset the parent and not include 'parent' in the selector in RockMigrations::createPage().
  14. +1. I am assuming there is something peculiar about options fields that makes this tricky, but it seems like this functionality was left unfinished some years ago.
  15. I have a situation where $templates->setImportData() is not behaving as expected. This might be more general issue as I can't see anything odd about my situation. Basically, the problem is that it seems to return the wrong result and not update the template, but returns no error messages. RESOLVED: The problem with the changes being truncated is that Tracy appears to truncate the json string. The php variable is fine. So maybe there is a Tracy problem, but nothing wrong with the API. The problem with not saving is that setImportData is not intended to save the data. I borrowed the saveItem() method from ProcessTemplateExportImport and that does the trick. Original problem description retained below for the record. .............................................................................................................. I have tracked the results using Tracy bd(), as follows: Template data is exported as json into a file: // Get the export data $rawData = $templates->getExportData($t); // $t is a template //Encode the array into a JSON string. $encodedString = wireEncodeJSON($rawData, true, false); //Save the JSON string to a text file. file_put_contents('json_array.txt', $encodedString); //Retrieve the data from our text file. $fileContents = file_get_contents('json_array.txt'); //Convert the JSON string back into an array. $decoded = wireDecodeJSON($fileContents); bd() indicates that $rawdata and $decoded are identical. A simple mod is made to the context description of a field ('operator') in the txt file and the above is run (without the file_put_contents, so as not to revert the txt ?), followed by $result = $templates->setImportData($t, $decoded); bd($result) gives (identical) 'old' and 'new' strings for the 'summary' field only (the first after 'title', which has no contextual changes). The remaining fields (including 'operator' which would be next) are not shown. array 'fieldgroupContexts' => array 'old' => '{ "summary": { "description": "About this page", "editRoles": [ 7258 ], "flagsAdd": 224, "rows … 50 } }' 'new' => '{ "summary": { "description": "About this page", "editRoles": [ 7258 ], "flagsAdd": 224, "rows … 50 } }' 'error' => array (0) Tracking this through the wire code, everything is as expected until Fieldgroups::___setImportData() where we have the following code (with my bd() additions): $old = wireEncodeJSON($_data['contexts'], true, true); $new = wireEncodeJSON($data['contexts'], true, true); if($old !== $new) { bd($old, 'old'); bd($new, 'new'); bd(trim($old), 'trim old'); bd(trim($new), 'trim new'); $return['contexts']['old'] = trim($old); $return['contexts']['new'] = trim($new); bd($return, 'return'); $old and $new are exactly as expected, with my change for the 'operator' description. trim($old) and trim($new) are the same as $old and $new. However, $return['contexts']['old'] and $return['contexts']['new'] have truncated the $old and $new strings as described earlier. bd($return) shows: array 'fields' => array 'old' => '' 'new' => '' 'error' => array (0) 'contexts' => array 'old' => '{ "summary": { "description": "About this page", "editRoles": [ 7258 ], "flagsAdd": 224, "rows … 50 } }' 'new' => '{ "summary": { "description": "About this page", "editRoles": [ 7258 ], "flagsAdd": 224, "rows … 50 } }' 'error' => array (0) In addition to the returned changes being incomplete, the template is not being updated for the import data. Initially I thought that these issues were connected, but on inspection, it seems that the the contexts are all set, regardless of any detected changes. Later in Fieldgroups::___setImportData() we have (again with my bd() addition) foreach($data['contexts'] as $name => $context) { bd($data['contexts'][$name], 'Context for ' . $name); $field = $fieldgroup->getField($name, true); // in context if(!$field) { if(!empty($context)) $return['contexts']['error'][] = sprintf($this->_('Unable to find field to set field context: %s'), $name); continue; } $id = $field->id; $fieldContexts = $fieldgroup->getFieldContextArray(); if(isset($fieldContexts[$id]) || !empty($context)) { bd($data['contexts'][$name], 'Setting context for ' . $name . ', id = ' . $id) ; $fieldgroup->setFieldContextArray($id, $context); $fieldgroup->trackChange('fieldContexts'); } } The dumps indicate that all the data is present and correct and that the new context for operator has been passed to setFieldContextArray. However, the template itself (on inspection in the admin) appears to be unchanged. I can see that there is a saveContext() method for Fieldgroup and Fieldgroups classes, which I can't see being called, so I'm not sure how the context is actually being saved. I'm afraid that, at this point my knowledge of PW (and, to an extent, of PHP) hindered me from making further progress, but maybe I missed something obvious along the way? Do I misunderstand the setImportData() method, might there be something odd with my data, or is there a bug?
  16. Thanks @bernhard, that's really helpful. It is prompting me to re-think my whole approach to PW development. I had already moved to wholesale use of template page classes; If I understand you correctly, you are now suggesting that I use the Page Class to hold the complete template definition via migrate(). And that I use that rather than the admin tools to maintain the template. Your first comment makes even more sense in that context. You will appreciate that, of course, the original app was not developed in that way - hence my need to copy and paste out of Tracy (a quick way of doing that would make moving onto your way of doing things much easier). I will read your post again and try a few things to make sure I have an approach that works for me. Meanwhile a couple of things occur to me: The way RockMigrations has eveolved (and maybe it's still evolving...) has meant there there is not a consistent documentation of the whole approach and the related API. This is not a criticism - I appreciate that you are just sharing something and are not obliged to document it completely and also that, while it is evolving, documentation is a painful overhead. However, being forgetful, I will need to document something for myself, so happy to share if it is useful. Exploring this module and reading your comments has raised a whole bunch of questions in my mind about the direction of PW. There has been much discussion lately about front-end enhancements, but I think attention should also be paid to the back-end and in particular to the issue of maintainability which your module addresses. I think there is a place for both UI-based development via the admin and code-based development like you suggest (more like a trad PHP framework). In particular the UI-based tools are an easy entry point for new users and for prototyping, so a route from that to the code-based approach would be helpful (hence my comments re Tracy copy & paste). I'm still quite a PW newbie and a fairly inexperienced PHP user, so I'm a bit nervous about putting this into the PW roadmap discussion without having a clear idea of the implications, but it seems like that is where it belongs. If you or other key players such as @ryan or @adrian want to pick up on this then that would be great.
  17. I'm assuming from the 'like' that I am thinking on the right lines. I've started building the migration module and it's coming along nicely - a dream to test it as you go along. Now for a bit of heresy ?. I wanted to migrate my Property template. I added the field website2, then I wanted to define the new state of the template with the new field, the amended widths etc. I couldn't quite remember what I changed (although not too hard to work out in this instance) but why should I? - surely I could just declare the whole thing as set out in Tracy's fieldgroupContexts for the template. But now followed a bit of awkward copying and pasting from Tracy's Request Info panel. Why not have a php script to interrogate the current (dev) state and create the required array...? Oh dear - I started by building a simple migration and now am in danger of disappearing down a rabbit hole... I suspect this could become a bigger issue than just this module. I see that @Kiwi Chris has had similar thoughts over on the thread that sparked my interest in this.
  18. Hi @bernhard. Prompted by your comments here , I decided to give this module a go as it looks very useful. One thing I find rather confusing is that you seem to have changed the suggested methodology and it's not entirely clear to me what is now the suggested best practice for using the module. From what I can see, you are suggesting that users construct their own module (along the lines of your example). Installing this will then run the migration and uninstalling it will do a downgrade. For further migrations, it seems that you are suggesting just adding to this module, not creating a new module. Your previous approach seems to have been more version-based. I am trying to understand how that works in practice. I currently have 2 migrations that I want to carry out. The dev system reflects the end state of both. I want to do it in 2 stages so that I can throroughly test the first migration before applying the second (although I think there is no dependency, it is better to do them in the same order as on the dev system). My plan is therefore to create the first migration module, back up the dev database, load the live database to the dev system, install the migration module to this, then test. If that is OK, then, if I understand correctly, I should amend the migration module to include the second migration and refresh the modules to run it. I would then test again. Now, what if there is a problem with this second migration and I want to reverse it? Uninstalling the module would presumably undo both migrations (unless I change the uninstall method to be only partial, which seems inconsistent). Sure, in the dev system, I could just revert to a backup, but that would not be a solution if the problems happened in the live environment as (unknown) data will also have been updated - in that case I would need to just be able to reverse the last migration. So should I put the second migration in a separate module? In which case, this starts to look more like your initial approach (except that the use of the migrate() method allows for a more declarative style). Am I clear in explaining my confusion? If so, can you de-confuse me? By way of background, here is my first migration plan (as a set of human instructions), to give you an idea about what I am trying to do: 1. Add new fields to templates: a. Property -> operator b. Property -> website2 (new field req’d) c. BookingForm -> propertyPage d. home -> taxYear, dayMonth, month & website2 (with descriptions) 2. Amend home template for a. description of operator (& webmaster?) & website (now allowed) b. re-use of summary field as restricted access field with description/instructions (and resize) c. re-order fields as per dev system 3. Change title of home to “System” 4. Add/amend hanna codes for a. ManagementMember b. Operator 5. Edit cheque payment method to = that on the dev system 6. Add images and files pages to home 7. Change title of home 8. Re-purpose comparison field as “mailHeaders” and add to StandardMails template 9. Remove bodyTop field from StandardMails template – change body to 10 rows, summary to 3, footer to 2, update descriptions & notes in the template and update instructions in the page I think I can see that there are methods for most of that in your module, but I'm not sure about the hanna codes. The php in all my hanna codes is identical (as it just passes data to a separate script), but obviously attributes etc. are different. I don't think RockMigrations handles these, but they have convenient export/import features so that's not too much of an issue. The second migration is 1. Use findPages to detect all pages with BookingStatus in a body field (i.e bodyTop etc. as well as body – any field that can take a hanna code) – approx. 15 pages. Make appropriate changes. 2. Change the refNumber for cancelled statuses (2 pages). I can probably turn these human instructions into php (although it may be quicker just to do it in the admin, but then I definitely do not have a quick 'downgrade option!).
  19. Hmm. Thanks, but that just seems to just resize the unformatted column. Also, if you drag it under the Tracy bar at the bottom, then there doesn't seem to be a way of getting it back ?
  20. Simple question: Is it possible to resize the field settings column when using Request Info on page? If you expand the array you get a very long thin column and I can't see how to resize. Thanks.
  21. ? I was going to mention the 2 migrations modules, but they are not part of the core. I have taken a look at RockMigrations and will do again - it's just that my last migration was too messy to use as an initial attempt. I will try using it for a simple migration first, to get the hang of it.
  22. I chose to use PW precisely because of its back-end capabilities - it facilitates quick and effective building of back-end applications. I've used it for a few sites now - one (a holiday cottage booking system) is an almost completely submerged "iceberg" which has hardly any front-end pages and even those are access-controlled - e.g. for use by other front-end apps. In that one, I have a similar issue to yours with customer pages. Of course, all the pages and names are unique, but that is not the real issue, which is to say precisely when a combination of fields should determine that a new page is added rather than an existing one updated . My solution is to have a hidden field (uniqueId) which is a concatenation of first name, last name, email address and postcode. So I have this in the before Pages::save hook: case 'Person' : if ($p->id) { $p->of(false); $postCode = ($p->address) ? $p->address->postal_code : ''; $p->uniqueId = wire()->sanitizer->selectorValue($p->firstName . '_' . $p->lastName . ' ' . $p->email . ' ' . $postCode); // Prevent duplicate records if ($p->parents("name=contacts")->count()) { $uniqueMatches = wire()->pages->find("has_parent=/contacts/, template=Person, uniqueId=$p->uniqueId, id!=$p->id, include=unpublished"); if ($uniqueMatches->count() > 0) { $this->warning("Change to contact record not saved - Unique id: " . $p->uniqueId . " already exists. (Use search box to find it). If you just created this record, please delete it if it is a duplicate."); $event->replace = true; $event->return; } else { $event->arguments(0, $p); // Save the unique value, overwriting any previous value } } } break; One might argue that the uniqueID is superfluous and that the key should be assigned to name instead, but my preference was for a hidden field. Another site is less of an iceberg (about 30% above the water) and is a membership system whereby members can access their records only through the front end via an email challenge protocol (like a password reset every time - a lot of the members are quite old and struggle with passwords). There are various admin access permissions for different roles within the club (membership secretary, treasurer, event organiser etc.). This one just uses the email address as the unique id - an approach not possible for the first site as not all customers book on line. I think PW is admirably suited for this type of application - I researched a whole bunch of other CMS/CMFs and nothing came close. The balance of features vs flexibility is spot on. The only issue that irks me slightly is the database vs. code issue - i.e. the database effectively contains business logic as well as data which does create maintenance issues (some careful thought is required as to what logic should be capable of being amended in the admin and what is best to put in the code if possible - for example I moved all Hanna code logic into a separate php script). I suspect the recent debate about front-end enhancements is precisely because PW is seen as a bit more lacking in that department.
  23. Resolved by adding the following to admin.js $('.pw-sidebar-nav').on('click', '#topPropertySelectForm', function(event) { // // as of Uikit beta 34, clicking items closes the offcanvas unless the following line is here event.stopPropagation(); }); where #topPropertySelectForm is the page select form.
  24. Thanks - I had come to the same conclusion. The following appears to achieve the same result as .load() but works when the div is outside pw-content-body var id = '#wrap_topPropertySelect'; $.ajax(window.location.href, { headers: {'X-Requested-With': 'justgivemethedanghtml'}, //:) dataType: "html", type: 'GET', cache: false, success: function (result) { var val = $(result).find(id).html(); $(id).html(val); } } )
×
×
  • Create New...