-
Posts
69 -
Joined
-
Last visited
Everything posted by jordanlev
-
@pideluxe - I think we are in agreement. I am not judging the entire PW system on the ability to create custom application functionality... all I did was explain where the limitations are when trying to do so (again, because the OP asked me to, not because I'm actively trying to give PW a bad name or anything). @Beluga - I unfortunately cannot do this because the data contains customer names and contact info. And I unfortunately don't have enough spare time to go through and anonymize the data or recreate it with dummy data (sorry, I agree it would be a good case study though).
-
@LostKobrakai - I wouldn't consider "more than one person working on a site" an advanced use case. In my experience, most CMS's suffer from this problem, so it's not something that is unique to ProcessWire -- but it seems to me that for some situations, storing the *definition* of the field structures in a config or code file, while keeping the data in the database is ideal. I utilize this system with a plugin I offer for the Concrete5 CMS where designers can set up their own content field definitions via a point-and-click dashboard interface, but the results of their pointing-and-clicking is saved to a php array. The data itself is *not* normalized into separate database fields though... so there is definitely a tradeoff (the plugin I make is best suited for page content that is not queried in any context other than "give me all the content on this one page that is being viewed"), and I don't think the way ProcessWire is structured would allow for such a setup. But other CMS's do utilize this approach -- Bolt CMS and October CMS come to mind, as well as the flat-file CMS's such as Statamic and Kirby. So I'm not suggesting that ProcessWire should (or can) be changed, but it is a limitation of building an application in PW. And another reason that if you're building a "custom application" as part of the larger website, you might want to consider using plain old SQL files or a db migration library and do it the old fashioned way instead of leveraging ProcessWire's "everything can be a page" approach.
-
@kongondo - I think we are saying the exact same thing, but perhaps my words have more of a negative spin than yours do. I agree with @Sephiroth that there seems to be a "mantra" on the forums that "everything is a page" and that the built-in tools can handle anything and everything... I think this can hurt newbies like myself who go down the wrong path. I just wanted to have a contrary opinion out there so people can see the "boundaries" or limits of the system and make informed decisions about their projects. (Also the OP specifically asked me for my thoughts via a private message @Sephiroth - I would be delighted to have a more detailed discussion about these things... I am *very* opinionated about how I think CMS's should work What is the best way to engage in a conversation about this? (I'm not sure I'd be able to just write it all up, but if it was more of a back-and-forth conversation or answering specific questions I can certainly do that). The last thing I will say is that all of you people here on the forums are wonderful and if ProcessWire continues to grow and become successful, it will be as much because of the helpful and friendly community as it is because of the technical underpinnings. So keep up the great discussions! Thanks, Jordan
-
@matjazp , for my custom application that I recently made in ProcessWire, I assumed that leveraging the built-in ProcessWire tools would save me a lot of time because I would not have to code up my own "CRUD" forms (dashboard forms for the site owners to add, edit, and delete records). I created "templates" and "fields" instead of creating my own database tables and fields. Many people had suggested that utilizing the PageTable field type would give me a ton of flexibility in how I structure my data relationships and turning those into editable interfaces without requiring any programming on my part. However, I ran into a few problems. The first big problem is that I did *NOT* want this portion of the application to be managed via the "pages" sitemap tree in the dashboard. I know that it can be very flexible and would save me a lot of development time, but it does not provide the streamlined and intuitive interface for my clients that I like to deliver to them... it just doesn't make sense to them why they would be editing their products in the same exact kinds of forms as their content pages. In addition to the sitemap tree not making sense for this use case, there are just too many tabs of fields all over the place, and it was difficult to understand how (or if it was even possible) to customize this editing interface for my clients. So, what I wound up doing is creating my own dashboard pages so I could customize the forms exactly the way I wanted. This was okay once I figured out how to "carve out my own niche" in the dashboard, but it took a lot of work. It was difficult to leverage the built-in "input" modules so that I could avoid building my own form logic (see this post for some details on how to make it even possible, because out-of-the-box it doesn't work without some modifications: https://processwire.com/talk/topic/8061-problems-with-image-uploads-when-including-processeditpage-form-on-another-admin-page/ ). The 2nd problem I ran into is that all of the template and field definitions are stored in the database, as opposed to config files or code itself. I understand the benefits of this in terms of making certain kinds of applications very easy for non-programmers to build because they can "point and click" their way to defining a data structure. But for my team this was a big challenge because it means we can't keep the actual data schema and template/field settings in version control. We had to pass around a database from designer to developers, and back and forth from staging servers and development machines and the live server. The 3rd problem I ran into is when it came time to do aggregate reporting. As I alluded to in that other thread you linked to, the ProcessWire system is basically a "database within a database", where each field is its own table. I had a fairly complex schema (although not totally crazy in my opinion) where I had customers, delivery locations, delivery schedules, orders, line items, and products all combined into one list (basically "show me all of the products in all of the orders that are to be delivered to customers on day x at location y"). This site has tens of thousands of orders per month, so the query was dog-slow (like, taking 1-3 minutes to run). What I had to do was spend a LOT of time "untangling" the ProcessWire query logic and write my own SQL query with a lot of JOINs in it... after I made this custom SQL query it ran in under 1 second! So, at the end of this project what I realized is that as a programmer who already has a lot of experience building my own forms and my own "CRUD" interfaces and my own database schemas and SQL queries, as well as my business being based on delivering very customized user interfaces to my clients... ProcessWire had nothing to offer me on the "custom dashboard application" side of things. All it did was put some new layers of abstractions that I had to learn and then work around in my way, when what I really wanted was to build things the tried and true way that has worked well for me for over a decade (HTML forms, PHP form processing via a "controller" of some kind, and a database schema with SQL queries for complex reporting). All that being said, I did not use the "Form Builder" that others are talking about, but I'm not sure that would offer the functionality you're needing in a dashboard interface (maybe it does, I don't know -- haven't used it). Also I understand that my requirements may be a bit more advanced than others... if someone did need to build out a custom application and was okay leveraging the built-in tools and compromising on a rather clunky editing interface, then ProcessWire would be an amazing tool to use. And of course I'm only talking about custom applications here... in terms of the templating and the front-end portion of the site, working with PW is a dream come true... I gladly await the day when every CMS and platform out there has the API consistency and "stay out of my markup"-ness as ProcessWire
-
@triples, I run a lot of C5 sites, and have no problems with speed on version 5.6.x. I believe that version 5.5.x had some issues with caching, but they seem to have been resolved with the 5.6. release. I haven't yet tried the newest version (5.7) because it has some usability issues that need to be worked out, and I've also heard reports that the speed isn't there yet. So, I'd say the 5.6 version of C5 is just as fast as any CMS I've used. Of course, any system can be made to run faster or slower depending on how you use it. For example, I built an eCommerce site with ProcessWire a few months ago, and I went along with the common advice here that everything should be treated as a "page"... each customer is a "page", each order is a "page", each product is a "page", each invoice line item is a "page" (none of these pages are displayed on the front-end, but in terms of data architecture that is how I have it set up). This turned out to be a very bad decision because of the fact that every field is actually its own table in the database, it makes reporting abysmal. I had to deconstruct the PW query builder and come up with my own SQL query to get any kind of reasonable performance on the "order reports" page... and it is like 20 JOINs in one query. The code is now not very easy to understand which is going to make maintenance more challenging then if I had built out a normal schema with normal database tables/records (instead of trying to shoehorn my data model into PW's "everything is fields+templates+pages" system). I don't mean to bash ProcessWire... but my point is that any system can be made to run faster or slower depending on if you're using it for the things it was designed for or not (and if you do things efficiently or not). Comparing one system's demo site to another system's real-world buildout is not a fair comparison. All in all, I'd say that ProcessWire's strong points are definitely its consistent and well-thought-out API, and its incredibly clean templating. But the editing interface is very clunky (although I know this is a matter of subjective opinion) and if you don't want "everything to be a page" then all the built-in field functionality isn't very easy to use in other contexts. Best, Jordan
-
Massive quantity of empty folders in site/assets/files
jordanlev replied to jordanlev's topic in General Support
Mystery solved: I actually *did* have an image field associated with every template because I had a "meta image" field (for facebook/pinterest shares etc) as a GLOBAL field... so I need to un-global that field and manually go through and assign it to only the pertinent templates. Hopefully then all the empty folders won't be created. -
I've had a site up and running for a few months now, and I just peeked inside the /site/assets/files directory and there are over 27,000 directories in there! Most of them are empty, and my guess is that Processwire is creating a new directory for every "page" in the site. (99% of these "pages" are not for front-end, but rather are data records representing customers/orders/line-items/etc -- but none of these have image or file fields associated with them). I found this forum post from 2012: https://processwire.com/talk/topic/1585-module-clean-empty-directories-from-siteassetsfiles/ ... where Ryan says that the core was updated (a couple of years ago now) to not create folders for every page, but it seems that it's not happening for me. I'm running version 2.5.3, so it's relatively recent. How do I keep this from happening? Is there a setting somewhere I missed? Or do I need to schedule a cron job to clean out empty folders periodically or something? Thanks, Jordan
-
I have a template for 'order', and another for 'order-item'... each order is like an invoice and each order-item is like a line item on the invoice (for a specific product). What I'd like to do is retrieve all orders that contain a certain product, and I'm not sure how to achieve this in an efficient manner. The naive approach would be to do something like this: $find_this_product = $pages->get('template=product, name=the-product-I-want'); $order_items = $pages->find("template=order-item, order_item_product=$find_this_product"); $orders = array(); foreach ($order_items as $order_item) { $orders[] = $order_item->belongs_to_order; } Problem with that (I'm guessing) is it's going to make a separate database query for each order_item. To reduce the number of queries, I'm thinking I could instead loop through the "order_items" and combine their order id's into a pipe-delimited string, then use that string of ids as a selector (e.g. $pages->find(id=1|2|3|4|5))... but that seems kind of yucky to me (especially since there might be hundreds or thousands of orders for a particular product). Ideally what I'd like to do is recreate this kind of SQL logic in my selector: SELECT DISTINCT orders.* FROM orders INNER JOIN order_items ON orders.id = order_items.order_id WHERE order_items.product_id = X; Is this possible to do in an elegant fashion with the processwire query engine? Or do I need to resort to SQL to grab all the ids and combine them into a string? Thanks. EDIT: I realize my title is a bit confusing. When I say "children", I mean "conceptual children"... the orders and order_items (and products) are related to each other by a Page field... I am not necessarily connecting them by parent/child location in the sitemap.
-
Below is some full working code for a process module that manages pages of a specific template. To install it, create a new folder in your site's "site/modules" directory called "ProcessCustomPageAdmin", then inside that new directory create a file called "ProcessCustomPageAdmin.module" and paste the code below into that file. Then install from your site's admin back-end (you'll need to "refresh" the directory to get PW to recognize it, just like with all new modules you try to install manually). A few things to note: The "create" and "delete" actions respond to GET requests, which is possibly dangerous. The "create" action is probably ok because this is behind your admin backend so you don't have to worry about webcrawlers accidentally hitting the pages. But depending on how important the data in the pages is, you might want to make it respond to a POST request only and put up some kind of confirmation. I was trying to keep things as simple and focused on your question as possible so I didn't include any of that stuff. There is no functionality to sort the pages. You need to output your own markup for the list view if you want to achieve this and then wire up your own "sort" action and set up JQueryUI sortable on the list display... way outside the scope of this forum topic here. You'll want to output this snippet of CSS on that page to hide the id that gets outputted for the CKEditor to function. Create a "ProcessCustomPageAdmin.css" file in your module directory and PW will automatically load it for you.#Inputfield_PageIDIndicator { display: none; } Last but not least, you need to change the $template_name and $parent_page_selector variables in the module class to suit your needs. Best of luck! <?php class ProcessCustomPageAdmin extends Process { public static function getModuleInfo() { return array( 'title' => 'Custom Page Admin', 'summary' => 'Dashboard interface for managing pages', 'version' => '100', 'author' => 'Your Name Here', 'permission' => 'page-edit', 'page' => array( 'name' => 'custom-page-admin', 'title' => 'Custom Page Admin', ), ); } private $template_name = 'your-template-name'; private $parent_page_selector = 'template=parent-page-template'; //determines where new pages are added under (can be any page selector) public function execute() { $table = $this->modules->get('MarkupAdminDataTable'); $table->setEncodeEntities(false); $table->setSortable(false); $pages = $this->pages->find("template={$this->template_name}, sort=sort"); foreach ($pages as $page) { $table->row(array( htmlspecialchars($page->title), "[<a href=\"{$this->page->url}/edit?id={$page->id}\">Edit</a>]", "[<a href=\"{$this->page->url}/delete?id={$page->id}\">Delete</a>]", )); } $table->action(array('Create New Page' => "{$this->page->url}/create")); return $table->render(); } public function executeCreate() { $page = new Page(); $page->template = $this->template_name; $page->parent = $this->pages->get($this->parent_page_selector); $page->title = 'Untitled Page'; //note that we don't need to set a name because it is autogenerated (as per the parent template settings) $page->save(); $this->session->redirect($this->page->url . '/edit?id=' . $page->id); } public function executeEdit() { if ($this->config->ajax) { return $this->modules->get('ProcessPageEdit')->execute(); //ajax-y form widgets (e.g. image uploader) need to be handled by the real ProcessPageEdit module (but for some reason they look to the form action for their ajax url instead of always going to /[admin]/page/edit) } $page = $this->getPageById($this->input->get->id); $action = "{$this->page->url}/edit?id={$page->id}"; $form = $this->buildPageEditForm($action, $page); if ($this->processPageEditForm($form, $page)) { $this->setPageNameFromTitle($page); $this->session->message('Page saved.'); $this->session->redirect($this->page->url); } Wire::setFuel('processHeadline', $page->title); return $form->render(); } public function executeDelete() { $page = $this->getPageById($this->input->get->id); $page->delete(); $this->session->message('Page deleted'); $this->session->redirect($this->page->url); } /*** HELPER FUNCTIONS ***/ private function getPageById($id) { $id = (int)$id; if (empty($id)) { throw new Wire404Exception('Missing or invalid page id'); } $page = $this->pages->get($id); if (!$page || ($page->template != $this->template_name)) { throw new Wire404Exception('Unknown page (it may have been recently deleted or had its template changed)'); } return $page; } //pass in empty string for cancel_button_url to not include one private function buildPageEditForm($form_action, $page, $include_save_button = true, $cancel_button_url = '') { //code inspired by: https://gist.github.com/somatonic/5011926 $modules = wire('modules'); $form = $modules->get("InputfieldForm"); $form->method = 'post'; $form->action = $form_action; foreach($page->getInputfields() as $input_field) { $form->add($input_field); } if ($include_save_button) { $form->add($modules->get("InputfieldSubmit") ->set('name', 'submit') ->set('value', 'Save') ); } if (!empty($cancel_button_url)) { $form->add($modules->get("InputfieldButton") ->set('name', 'cancel') ->set('value', 'Cancel') ->set('href', $cancel_button_url) ->set('icon', 'times-circle') ->set('class', 'ui-button ui-widget ui-corner-all ui-priority-secondary') //gives it a faded look ); } //IMAGE UPLOADER WIDGET NEEDS THIS TO FUNCTION PROPERLY (note that the "name" is only so we can hide this via CSS... but the only thing the image uploader needs is the p tag): $form->add($modules->get("InputfieldMarkup")->set('name', 'PageIDIndicator')->set('value', "<p id='PageIDIndicator'>{$page->id}</p>")); //CKEDITOR IMAGE BUTTON NEEDS THIS TO FUNCTION PROPERLY: $form->add($modules->get("InputfieldHidden")->set('name', 'id')->set('value', $page->id)); return $form; } private function processPageEditForm(&$form, $page) { //code inspired by: https://gist.github.com/somatonic/5011926 $input = wire('input'); if (!$input->post->submit) { return false; } $form->processInput($input->post); if ($form->getErrors()) { //pass true to clear the errors return false; } $page->setOutputFormatting(false); foreach($form as $field) { $page->set($field->name, $field->value); } $page->save(); return true; } private function setPageNameFromTitle($page, $append_number = null) { $name = wire('sanitizer')->pageName($page->title); if (!is_null($append_number)) { $name .= '-' . (int)$append_number; //yes, we want to explicitly cast to int and then implicitly cast back to string (via concatenation) } if ($name == $page->name) { return; //avoid the page finding itself as a dup in the query below } else if (wire('pages')->count("parent=$page->parent, name=$name")) { $append_number = (int)$append_number + 1; $this->setPageNameFromTitle($page, $append_number); } else { $page->set('name', $name)->save(); } } }
-
Well, after a ton of debugging and stepping through code, I've begun to understand what the problems I was having are. Unfortunately these require very specific fixes to the way that specific inputfields work (there's not some global setting), so I would imagine that there are other field types I haven't gotten to yet that will cause problems as well. But for what it's worth, here is how to fix the issue with the image uploader widget and the CKEditor image button: IMAGE FIELD (2 things required): * The image uploader needs to make an ajax request to the ProcessPageEdit page (/processwire/page/edit), but instead of using the actual url to that page it retrieves the form action. Since I've changed the form action to post back to my own page, this was resulting in the ajax call not getting what I needed. To fix this, I added this at the very beginning of my "executeEditMyPage()" function: if ($this->config->ajax) { return $this->modules->get('ProcessPageEdit')->execute(); } * The image uploader needs to know what page it is working with, so add this somewhere on the page (it can live anywhere on the page, but I'm adding it as an "InputfieldMarkup" field to the form so everything is self-contained there): $form->add($modules->get("InputfieldMarkup")->set('name', 'PageIDIndicator')->set('value', "<p id='PageIDIndicator'>{$page->id}</p>")); (note that the image uploader only needs that p tag, but I am also setting a "name" on the inputfield so that the generated markup gets an id I can then hide via css, like so: #Inputfield_PageIDIndicator { display: none; })CKEDITOR IMAGE BUTTON (1 thing required): * The ckeditor image plugin needs to know what page it is working with, but it looks for a different element than the image uploader widget does. So add this to the form as well (this actually needs to be a form field): $form->add($modules->get("InputfieldHidden")->set('name', 'id')->set('value', $page->id)); VOILA!Wish me luck when it comes time to figure out Repeater and PageTable
-
I just thought of a more succinct way to ask my question (for those who don't want to read through my long rambles): Does anyone ever use the built-in "image" inputfield on pages other than ProcessPageEdit? What about the CKEditor? If so, how exactly do they make those work outside the confines of the specific markup on the ProcessPageEdit admin page? Do I need to surround them with specific elements/classes/id's? Or do I need to make sure certain JS files are included (other than the ones that the inputfield module outputs itself automatically)? Surely I'm not the first person who wants to allow image uploads outside the context of a normal page edit operation?
-
Thanks for the response. I will give your suggestion to extend ProcessPageEdit a shot, although I will try to explain exactly what I'm wanting to do here. The situation is that I have a somewhat complex data model. Products are being sold on specific dates in specific locations, and at each location there are different product variations (or "product options"... like different sizes, each with different prices). Kind of like a company that goes around to farmers markets or fairs and sells things, but depending on the season and what they have in stock, they might have certain products available one week but not the next, and also they might need to charge different prices at different weeks depending on how hard it was to get something. (Also also, they might have different product variations/options available from week to week, because they might run out of one option or size but then get more in stock a few weeks later, etc.). It seems that the general consensus amongst the PW developer here is that I can use the built-in page tree as my editing interface for this data. It's true that I can do that, but it does not meet my standards for providing a "guided interface" to my clients who need to manage this data. Here are some examples of the things that I don't like about using the built-in pages interface for managing this data: There will be generic content pages on this site ("home", "about us", "terms & conditions", etc.), AND there will be pages that are populated by the product/location/dates-of-sale/variations/etc data. To the client, these are different things because the generic content pages are more free-form and might look very different from week to week, but the "data" stuff is much more structured and ties into their actual business (not just the marketing of the business). So I think it would be confusing to have to use the same "page tree" interface to manage both the pages of the site and the "data" of the site. Instead I'd prefer to have a separate section in the admin interface that is specific to the management of the "data", and then they just use the page tree for the generic content pages. This I am able to achieve very nicely via a ProcessModule (as I explained in my other recent thread here: https://processwire.com/talk/topic/8043-how-to-provide-admin-interface-for-editors/?p=77757 ). Using the built-in "page edit" admin pages burdens the client with too many options. The only tab I want them to see is "content", not "settings" or "delete". I don't want users to have to see the "page name" field, because these are pages that don't correspond to pages on the site front-end... so why should the user have to care about the url slug? I think there are ways around this one though with various settings. I can use the PageTable field to manage the "sub-records" (for example, the product variations that are applicable to a certain week-of-sale), but it does not give me the ability to enforce certain constraints. For example, in a particular week they will add the various locations that they are travelling to. The list of possible destinations exists as another template... so there is a "page" field that allows them to choose which location they are adding a sub-record for. But each time they add another sub-record (via the PageTable field), it shows them the entire list of cities again. This means they could possibly add 2 sub-records of the same city, which will cause problems. It just doesn't make sense in the data model that the same location would have 2 sub-records in a particular week. Another problem with the PageTable interface is it opens the sub-page in a modal dialog and it too has the tabs at the top. I *only* want the "content" tab to appear. And I can actually make this happen by going into the database for the template and adding "noSettings: 1" to the json data for the fields. But it still shows the "content" tab, even though it's the only tab. It takes up a lot of vertical real estate and is one of those things that isn't a huge deal but it's still less than perfect. In general, I think the "edit"/"move"/"view" buttons in the page tree are not very nice. Please don't hate me I totally understand that this is a matter of opinion, and most people seem to like them, so that's fine. But I think it would be better to have actual buttons next to each item in the list. So really what it comes down to is I want to leverage the individual "field" interfaces (inputfields), but not be forced to use them in the built-in ProcessPageEdit page or the Page Tree. I was hoping that because ProcessWire is so modular and well-thought-out, that this would not be a difficult thing to achieve. But perhaps this is not the case, which would be a bummer. Because then it seems my options are to give up and deliver a not-very-polished editing interface to my client, or re-build all of PW's functionality so I have the control to make things just the way I want (and at that point, I might as well not even use a CMS and just build a from-scratch app... which I *really* was hoping to avoid). Thanks for your time and assistance, it is greatly appreciated. I know I'm being very particular here, but the complete markup flexibility is what attracted me to PW in the first place, so I hope it's something the community can appreciate
-
Hi, I'm using this snippet from soma: https://gist.github.com/somatonic/5011926 to render and process a page's edit form on my own admin page (via a process module). It all seems to work very well, except there are some problems with some of the more advanced input types, like the image field's drag-and-drop file upload does not work, and using the PageTable field kinda-sorta works mostly but has problems rendering the table sometimes or it adds a new page but doesn't "connect" it to the parent properly. My guess is that these issues are related to missing javascript, but the css and js files for all of the inputfields are being outputted to the page. I've also made a call to wire('modules')->get('ProcessPageEdit') just to give that module a chance to output its own css/js as well (and when I look at the page source I see that its css/js files are in fact there in the header). Note that I am in the admin section here... this is not for "Front-end editing" or anything like that. Or perhaps there are some element classes or ids in the "ProcessPageEdit" display itself that the javascript expects to find? But how could this be the case since other people make admin themes so they can't assume that the overall page markup is going to be the same? Anyone have any ideas on how to get this to work? Thanks! EDIT: Also the "image" button on the CKEditor toolbar doesn't work (it pops up a dialog with a PHP error in it about 'pages' being null)
-
Well, it appears that the module *is* working... it's just that the button doesn't show up on pages of the "admin" or "home" template. Thanks for the tip @adrian... that is a cool feature (although not very intuitive... I didn't notice it until you specifically mentioned it... but maybe that's the point
-
How to provide admin interface for editors?
jordanlev replied to jordanlev's topic in Getting Started
Hey @soma, I see what you mean about having the pages outside of the "Admin" section... I wasn't sure I would be able to hide them effectively until Craig mentioned this in his response above. As for re-creating the editing interface... my issue is that I actually have a more complex data model than what I've used for an example in this thread. Specifically, a product can have "product variations" and "product locations". The product variations each have a name and a price (and some other miscellaneous info). The product locations also have additional info (so it's not just a reference to a location page from a product page). What I'm imaging is having sub-pages under each product for "variations" and "locations", then a page type for "product-variation" and another page type for "product-location". I'm concerned that it will get too tricky for my users to have to remember to add these various things in the right place. Instead I'd like for them just to be able to view a list of products, then click on a product to see a list of BOTH the variations AND the locations, and edit all that info in one place. (I know what you're thinking: I can have a "page" field that allows multiple selections... but again the problem is they aren't just choosing variations and locations, rather there is associated information pertaining to each one). So maybe there is a way to achieve this management of complexity via the built-in page tree? Maybe some kind of Table or PageTable field? I would be very open to hearing suggestions... which is why I posted this question in the first place (and am really glad to have gotten so much good info from you and the other guys). Last: Can you please explain in detail how to achieve "you can render part of the tree in your module and have them edit using pw edit screen and sort or delete them with a few lines of code"... I would *love* to see this code, if you could provide it. Thank you! -
How to provide admin interface for editors?
jordanlev replied to jordanlev's topic in Getting Started
Thanks for the great suggestions! @mr-fan, what a fantastic write-up of that first site you built -- gives me lots of ideas and new things to discover (like the "Template Decorator" and the "FormTemplateProcessor"). I wound up discovering a relatively easy way to achieve what I want, by creating a Process Module as follows: <?php class MyProductsAdmin extends Process { public static function getModuleInfo() { return array( 'title' => 'My Products', 'summary' => 'Dashboard interface for managing product data pages)', 'version' => '001', 'author' => 'Jordan Lev', 'permission' => 'page-edit', 'page' => array( 'name' => 'my-products-admin', 'title' => 'My Products', // 'parent' => 'setup', //commented out because we want to be directly under admin ), 'nav' => array( array( 'url' => 'products/', 'label' => 'Products', 'icon' => 'cubes', ), array( 'url' => 'locations/', 'label' => 'Locations', 'icon' => 'building', ), ), ); } public function execute() { return $this->render('menu'); } public function executeProducts() { $pages = $this->pages->find('template=product'); return $this->render('products', array($pages)); } public function executeLocations() { $pages = $this->pages->find('template=location'); return $this->render('locations', array($pages)); } private function render($template, $vars = array()) { $template_file_name = $template . '.' . $this->config->templateExtension; $template_file_path = $this->config->paths->MyProductsAdmin . "/templates/{$template_file_name}"; $template_file = new TemplateFile($template_file_path); $template_file->setArray($vars); return $template_file->render(); } } I found some really handy comments in /wire/core/Process.php file -- some features which I didn't find googling around or searching the forums -- basically you can create a sub-nav that corresponds to your various "execute" methods. Also note that I have a sub-directory called "templates" in my module directory, and this is where I can put the various templates I want to render for these admin pages (for example, "/templates/products.php" and "/templates/locations.php"). So this achieves one of my goals: it creates a new admin section that can be seen by people with the "edit-page" permission, and it has sub-pages underneath in the admin nav. The code I posted above is a simplified example... I actually implement a rails-like MVC system within this module (so each executeSomething() calls a controller class and passes it sub-actions for 'list', 'edit', 'delete', etc.)... but I wanted to keep this code simple for the sake of explanation. Note that this module is only for the "admin interface" to the data (pages)... the pages themselves don't actually live under this module, but rather I created a separate page under Admin called "my-products-data" and all pages representing my products and locations live under there. This admin interface module basically treats that "data" section as a database... users will *not* be interacting with those pages directly in the site pages tree, nor will end users ever see those pages (because they're under "Admin"). This is exactly how I want it (although I can understand that other people might prefer to set up their sites differently, so I'm not saying this is the best solution for everyone, but it works for me). The other half of my problem: listing / adding / editing / sorting / deleting pages... it appears that there is no automatic functionality in ProcessWire to achieve this. It looks like the new "ListerPro" module can handle this to an extent (it can list pages matching a certain selector, and provide actions to move/edit/delete them), and that would probably be a fantastic option if you don't want to have to do much programming. But I am very particular about the editing interfaces I provide to my non-technical clients: I want them to be as easy-to-use and straightforward as possible, and I think a downside to the PW admin is that you wind up with a slightly confusing interface (which is understandable because it needs to be all things to all people -- and it does a surprisingly good job at providing all these capabilities). So what I realized is it won't actually be too difficult for me to craft my own "CRUD" interface for these pages. Here's generally how I did this: top-level "index" page for products/locations: I retrieve all pages having the 'product' or 'location' template, and output them into a table. Each row has an "edit" and "delete" button. And at the bottom is an "Add New" button. Clicking the "Add New" button programatically creates a new page of the desired type and places it under the proper parent page in the "/admin/my-products-data" section of the sitemap. It assigns it the title "Untitled Product" (or "Untitled Location"), and the name is irrelevant (pw will auto-generate one, it doesn't matter because these pages only serve to store data... they are not used for display, and they will always be retrieved by id and not name). Then after that new page is created I redirect the user to the "edit" page. Clicking the "edit" button on a record (or being redirected here from the "add new" function) uses this technique from soma to display the page's form with all the defined fields: https://gist.github.com/somatonic/5011926 (but note that you only need the first part of that code... *not* the "head.php" part of it because we're already in the admin section so the necessary js and css is already loaded by the system!). Clicking "delete" on a record asks for confirmation from the user and then programatically deletes the page. Sorting: I've implemented my own drag-and-drop sorting using jquery UI sortable... upon a drag-drop, an ajax request is sent back to the module (I have a "execute" method that reponds to it) containing the ordered list of id's... this method then sets the "sort" field on the appropriate pages. Note that this is only feasible for lists where you show all "records" (pages) at once... if you had paginated the list it is probably too long to allow for sorting manually like this. So that's the basic idea... I did wind up having to recreate some functionality from scratch, but not much... the fact that I can leverage the "page edit" form using soma's code snippet means that the hard part is already taken care of for me The structure I have laid out here gives me a lot of flexibility to manage my pages exactly as I want them to be, while providing my non-technical clients a very intuitive interface that shows them only what they need to manage their data. I actually have a much more complex schema than just "Products" and "Locations" (I wanted to keep things simple for this question so I didn't bother mentioning it)... so fortunately this technique will accomodate all sorts of crazy structures (because I am programmatically adding the pages, so I can be sure the correct type/template is used and it is placed in the proper location of the site tree... and in some cases I am actually creating several pages of different types based on 1 user action... e.g. placing a new "order" will create both an "order" page and sub-pages for each "line item" in the order, etc.). Anyway, just wanted to write this up in case others (who think like me) want to do this in the future. Or if I forget about it in 6 months and need to read through some docs on it -
Hi, This is a great module (not sure why this isn't part of the base system functionality) -- thanks! I'm on PW 2.5.3, and when I first installed this module everything worked fine. But after a little bit, I no longer see the "delete" buttons in the page tree. I unfortunately can't remember what I did between it working and not working (I'm building my first site with ProcessWire so a lot of creating and deleting pages, moving things around, trying out modules, etc.)... But I even tried uninstalling this module and re-installing it, but it still just doesn't show those delete buttons in the page tree. Any suggestions? Thanks!
-
Finally setting up an actual site with ProcessWire, and I'm baffled about how to set up the following situation: * I have two data entities, "products" and "locations". So there will be a template for "product" and "location", and a product can be in any number of locations so I will use a page field to link the two. * I have users who are *not* super-admins, but rather in a group I created called "editors". These users should have access to edit site pages but not configure templates and fields. * I want to have 2 pages in the admin section that will contain the master lists of products and locations (the front-end of the site will use queries to display the appropriate data -- there will *not* be front-end pages representing these data entities, so I will house them under the admin section). My first problem is that I can't just create pages under the "Admin" page, because there is no way for my "editor" users to see those via the page tree. I tried creating a new template for those 2 top-level "products" and "locations" pages, so I could assign permissions on them for the editors to be able to see them and add new pages underneath, but that didn't work because the editor users were never shown the sub-pages under the top-level "Admin" page. (Also there was no way to render the page contents within the overall admin theme... it appears you can only do that via Process Modules). So I created ProcessModules for these 2 pages ("ProcessProductsAdmin" and "ProcessLocationsAdmin"), which can have permissions assigned in the module info, and also their pages will show up in the admin primary nav menu. So far, so good.far. But now how do I actually create the interface on these pages to allow users to manage the individual "product" and "location" pages underneath? I read some things about the new "Lister" module in 2.5 which looks like it might work, but in the demo video ( ), it shows an option to create a new lister, but this option does not appear on my site. I could re-create my own list of sub-pages, but then I'm having to do all of my own code for adding new sub-pages, editing, deleting, and re-ordering... which seems counter-productive (like, why am I bothering to have these data records stored in the "ProcessWire way" where they are pages and fields, if I'm having to write my own code to manage them instead of leveraging the functionality of the CMS?) I came across this forum thread, where people seem to be talking about what I'm trying to do (well, just the "edit a page" part, not the "list pages and allow adding new, re-ordering, and deleting" part): https://processwire.com/talk/topic/6090-page-edit-in-another-module/?p=59589 ...but you can see in the screenshot of the post I linked to that Tom Reno has kind of done what I'm asking to do... but I have no idea if that is just done with built-in functionality or if he went through the trouble to create his own code for that. Seems like I'm going around in circles here... I'm hoping there's a relatively easy way to accomplish this within ProcessWire (since the whole point of using a system like this is to try to avoid writing my own code that creates/edits/deletes/sorts records). Thanks!
-
How do you update a page with all its field via front-end?
jordanlev replied to Marc's topic in Getting Started
I unfortunately don't have an answer to your specific question (I'm rather new to ProcessWire myself), but have you seen the "Fredi" module: http://modules.processwire.com/modules/fredi/ It will handle the front-end editing for you. -
@mr-fan, thanks for the links... that is a fascinating discussion. @LostKobrakai , thanks for the info about the same field type having different schemas... I was not aware this was an option. Seems to me that the assumption in Processwire is that all fields have the potential to be "global" (available to all pages), hence this architectural decision. I'm not sure I agree that that's the best approach, but I do see the logic behind it. Also as with everything it depends on the kinds of sites one is building. Anyway, thanks for the info guys, much appreciated!
-
policy on php short tags in PW modules / docs?
jordanlev replied to jordanlev's topic in Getting Started
Thanks guys, I will avoid it for PW modules. I do like using them myself (the markup is more aesthetically pleasing to me when I can do <p><?=$something?></p> as opposed to <p><?php echo $something; ?></p>), but if enabling the short tags isn't part of the PW installation requirements, then I don't want to risk it breaking on anyone else's servers. -
I love how relatively simple PW is at its core, and how everything is built up from that foundation in an elegant way. Now that I've spent more time digging into the code and architecture, there is one thing that strikes me as curious: why does each field that you create get its own database table, instead of there just being 1 table for each field *type*? It seems to me that the schema of each field's database table would be exactly the same for all fields of 1 type, so what is the benefit of this extra layer of abstraction (separate tables for each field instance)? -Jordan
-
How to hook into template rendering to pass it variables?
jordanlev replied to jordanlev's topic in Getting Started
Thanks for the link -- that does look like what I was asking for. But are you saying that a Process Module's init() function only gets run when a user is in the backend/admin of the site, and not when front-end pages are being viewed? If that's the case, then I guess there's nothing I can do about this. Or is there a different kind of module type I can use that *does* get run on every page view, even the front-end? -
Renaming the "content" tab when editing pages of a given template
jordanlev replied to netcarver's topic in General Support
@netcarver, Would you mind sharing the code you used to successfully override the title (as mentioned in your "update 2")? It would be quite helpful to me. Thanks! -
Renaming the "content" tab when editing pages of a given template
jordanlev replied to netcarver's topic in General Support
Furthermore, does anyone know how I can actually removing the visual "tab top" altogether (not the contents of the tab, just the rectangle at the top). This is because I have a template set to have "no settings" ($template->noSettings = 1), so there is *only* the content tab being shown... and it is redundant and wastes space when I show the edit fields in a modal.