-
Posts
16,867 -
Joined
-
Last visited
-
Days Won
1,571
Everything posted by ryan
-
Adam, sounds like you have some good ideas. I have to build a contact form for a site that's going up tomorrow and think I'm going to use the Inputfield modules to do it (manually) just to see how it might work. I'll post the template and result here tomorrow if you guys want to see it. I think it'll lead to some more thoughts on a form builder.
-
I would like something like that too, though don't know how it could be done without being something that generates markup. But this is one of the cases where markup generation is probably desirable. I am thinking the best bet will be to base it on ProcessWire's Inputfield modules, since they are already used for this purpose in the page editor (and throughout ProcessWire). And they already generate their own markup and retrieve and sanitize their own values. The main thing will be to write an editor to construct the form (similar to the template editor) and then something to save submitted entries to the DB (or optionally, page records). I haven't actually tried Drupal's form builder yet, I need to get a look at that. Edit: I see Adam is thinking the same thing (and typing at the same time as me). It may be that the existing Templates system can be used for creating these forms as you mentioned.
-
Congrats to you guys on writing the first 3rd party ProcessWire module! I've added a Modules section on the download page. This will be built out more in the future, but just wanted to get something there for now.
-
Tried tried it out, and it's looking good! Only thing I noticed is that in your getModuleConfigInputfields function, you need to add "$field->value = 1;" to both showModal and initCollapsed, so that there is a value for the checkbox when it is checked. Also, in your README.txt I recommend a note to tell people that they need to name the module dir "AdminBar". This reason is that if you download the ZIP from GitHub, it's named apeisa-AdminBar-39820zvosomethingorother.zip, and when you it extract it, the dir has that name as well... so I had to rename that dir to be AdminBar in order to get it to work (ProcessWire requires that the dir and module name are the same, though maybe I should change that).
-
The Modules configuration stuff is fixed, and I also added some optimizations to the Modules loader while I was at it. Grab the latest master and upgrade before proceeding with the following. For AdminBar.module here is what I would recommend. Add a getDefaultData() function to the AdminBar class to store your defaults. This isn't part of a PW2 interface, this is just for your use, but it prevents you from having to name the defaults twice. <?php /** * Default configuration for our module * * The point of putting this in it's own function is so that you don't have to specify * these defaults more than once. * * However, there is no requirement for you to do this (it was just my preferred style). * If you preferred, you could just check $this->showModal and if it was null, then you * would know it had not yet been configured (and likewise for the other vars). * */ static public function getDefaultData() { return array( 'showModal' => 1, 'initCollapsed' => 0, 'administration' => wire('config')->adminRootPageID ); } Now setup your constructor to populate that default configuration data in your class. You want to do this just in case your module is running "unconfigured" (i.e. the admin has never gone in and tried to make any settings). <?php /** * Populate the default config data * * ProcessWire will automatically overwrite it with anything the user has specifically configured. * This is done in construct() rather than init() because ProcessWire populates config data after * construct(), but before init(). * */ public function __construct() { foreach(self::getDefaultData() as $key => $value) { $this->$key = $value; } } Next is your function that handles the config data. I changed it so that it pulls the defaults from that function we added above. Also, the checkbox fields need a value, so I just set that as "1". That's the value that gets used if it's checked. Lastly, since we're using HTML5 with XHTML tags, I set the 'checked' attribute to have a value of 'checked' rather than boolean true. Not sure that it matters, but this seems to be XHTML syntax best practice. <?php static public function getModuleConfigInputfields(array $data) { // this is a container for fields, basically like a fieldset $fields = new InputfieldWrapper(); // since this is a static function, we can't use $this->modules, so get them from the global wire() function $modules = wire('modules'); // Populate $data with the default config, because if they've never configured this module before, // the $data provided to this function will be empty. Or, if you add new config items in a new version, // $data won't have it until they configure it. Best bet is to merge defaults with custom, where // custom overwrites the defaults (array_merge). $data = array_merge(self::getDefaultData(), $data); // showModal field $field = $modules->get("InputfieldCheckbox"); $field->name = "showModal"; $field->label = "Show modal box?"; $field->description = "Whether edit opens modal box or goes to administration."; $field->value = 1; // providing a "checked" value for the checkbox is necessary $field->attr('checked', empty($data['showModal']) ? '' : 'checked'); $fields->add($field); // initCollapsed field $field = $modules->get("InputfieldCheckbox"); $field->name = "initCollapsed"; $field->label = "Init collapsed?"; $field->value = 1; $field->attr('checked', empty($data['initCollapsed']) ? '' : 'checked'); $fields->add($field); // administration field $field = $modules->get("InputfieldPageListSelect"); $field->name = "administration"; $field->label = "Where to go after clicking on 'administration'"; $field->value = $data['administration']; $fields->add($field); return $fields; } Lastly, your pageRender() method is currently checking $this->showModal with an isset() function. That's not going to be the right approach here because it's always going to be set once it's configured (it will be set with a value of either 0 or 1). So the thing to do is: <?php if($this->showModal) { // draw the modal link } else { // draw the regular link } ...and likewise with any other configuration options. None of the configuration options will ever be unset since in this class instance we're now setting them in the constructor. I tested it here using your module, and all appears to work from this end. Let me know how this works for you. I'm really enjoying using this module!
-
Any pointers on creating a mobile version of a site?
ryan replied to martinluff's topic in API & Templates
There is $config->httpHost, but it's exactly the same thing as $_SERVER['HTTP_HOST'] except that it's been sanitized. But in this case, it wouldn't matter which one you check. Edit: Also $config->httpHost wouldn't have worked in the database selection because it's set by the ProcessWire bootstrap class (which hasn't yet been instantiated when /site/config.php is loaded. -
I just tried out AdminBar and it works great! I love it. It seems like there is a need to have a save button that doesn't need to be scrolled to. Can buttons be added to the modal window? Or, I was thinking I should just add it to ProcessPageEdit (right next to the page ID at the top right), as it would be useful with or without the AdminBar. I'm working on the Modules.php update/fix right now and should have it posted to the master branch shortly. Nice work with AdminBar!!
-
Any pointers on creating a mobile version of a site?
ryan replied to martinluff's topic in API & Templates
Another possible approach would be to point m.domain.com to just domain.com. Then your head and foot templates (or main template if you are using that), would check $config->httpHost to see if it's at the m.domain.com, i.e. /site/templates/head.inc <?php if($config->httpHost == 'm.domain.com') include('./head-mobile.inc'); else include('./head-normal.inc'); Your head-mobile.inc would contain your mobile specific markup, stylesheet links, javascript links, etc. Likewise, your head-normal.inc would contain the same for the non-mobile version of the site. That way you wouldn't have to do anything on a page-by-page basis, and the mobile version of the site would just happen automatically depending on the hostname. What you would have to be aware of is that any markup created in your page-specific templates should be compatible with the mobile or regular version of the site. However, I think this would often be the case even if you weren't trying to do it that way. Though if there were instances where you needed them to be different, you would just check that $config->httpHost again from inside your template. If you are concerned about duplicate content at the m.domain.com, then using a canonical tag may be a good option to add to your head-mobile.inc: <link rel="canonical" href="http://domain.com<?php echo $page->url; ?>" /> -
Thanks, I can't wait to check this out! We are headed to dinner, so I'm going to check when I get back. If you are still around and want to try it, here is a fixed /wire/core/Modules.php that corrects the bug (attached). I am going to go over it in more detail tomorrow. Also, I may have told you the wrong thing on setting the defaults in the getModuleConfigInputfields... you may be able to remove that part at the top. I'll double check on the proper syntax either tonight or tomorrow, and then get started on the module documentation! Modules_php.zip
-
Looks like you've found a bug! I will work to get this fixed for tomorrow. Sorry for the inconvenience. Turns out this is the first configurable module of it's type, so it's not come up before. But I think it will be an easy fix.
-
You are right. I'm figuring it out now... you the first person to develop a module, and this is only the 3rd or 4th configurable module ever, so I'm getting up to speed myself. I'll reply shortly.
-
It's getting near the end of the work day and I'm forgetting crucial details. To read this values, they will be automatically set in your AdminBar instance. So anywhere in that class, you should be able to just reference it like: $this->showModal, and so on. If the value is not set or doesn't exist, it will just be null. If that's the case, then you should use some default value, because it means the module has never been configured. Or you could set them up in a __construct method. The constructor is of course executed before PW2 sets any values to the module. Whereas the init() method is called after PW2 sets values to the module. So you might setup your defaults like this: public function __construct() { $this->showModal = true; } No need to define a separate showModal var in your class, because WireData is already handling that for you behind the scenes...
-
Sorry, my bad. I learn something new every day. I'm admittedly ignorant on this matter (I live in the US, in backwoods Georgia, after all). Given what you've mentioned, I'm adding a new $config->dateFormat to the PW2 source. What do you suggest would be a good default? Something universal doesn't exist, but what would be the closest to something universal to serve as a default? I would like to talk more with Adam and you about more localization options for PW2 (maybe we can start a new thread soon).
-
There isn't. But the current practice is to use either SQL date format in places where the date need to be a fixed length and/or sortable with other dates (like in a table), and to use "January 27, 2011" or "Jan 27, 2011" format anywhere else. I believe these formats are fairly universal, though correct me if I'm wrong. This is to be determined, but on the roadmap. I think that Adam is giving this some thought, as am I, and all suggestions are welcome. Thanks, Ryan
-
Sorry about the double post. This is probably the only forum I've ever used, so I don't know what the etiquette is. Consider me new to this. Okay to make your module configurable, modify the class definition and add "ConfigurableModule" to it: <?php class AdminBar extends WireData implements Module, ConfigurableModule { To implement this interface, we need to add a module configuration function to your AdminBar class. It takes one parameter, which is an array of data (that ProcessWire provides to it). If the module has been configured before, that will contain the settings in simple key = value format. It expects you to return a value of type InputfieldWrapper(). Since that is only documented in the source right now, I wanted to get you started with an example: <?php static public function getModuleConfigInputfields(array $data) { // this is a container for fields, basically like a fieldset $fields = new InputfieldWrapper(); // since this is a static function, we can't use $this->modules, so get them from the global wire() function $modules = wire('modules'); // set some defaults if the values aren't already present if(!isset($data['showModal'])) $data['showModal'] = 1; // default to checked if(!isset($data['initCollapsed'])) $data['initCollapsed'] = 0; // default to unchecked if(!isset($data['administration'])) $data['administration'] = wire('config')->adminRootPageID; // default to admin page // showModal field $field = $modules->get("InputfieldCheckbox"); $field->name = "showModal"; $field->label = "Show modal box?"; $field->value = 1; $field->description = "Whether edit opens modal box or goes to administration."; $field->attr('checked', !empty($data['showModal'])); $fields->add($field); // initCollapsed field $field = $modules->get("InputfieldCheckbox"); $field->name = "initCollapsed"; $field->label = "Init collapsed?"; $field->value = 1; $field->attr('checked', !empty($data['initCollapsed'])); $fields->add($field); // administration field $field = $modules->get("InputfieldPageListSelect"); $field->name = "administration"; $field->label = "Where to go after clicking on 'administration'"; $field->value = $data['administration']; $fields->add($field); return $fields; } Once you've put this in there, you can go configure your module in the admin by clicking "Modules" and then clicking on your AdminBar module. You should now see the fields ready to configure.
-
Let me know what settings you want to start with, and I'll paste in an example for your settings.
-
Page::loaded isn't what you want. That is called after every page finishes loading it's initial data and is ready for API use. It doesn't mean that the page is loaded in the browser, just that it's loaded in memory on the server. This is what you would use if you wanted to examine or modify some value on every page that gets loaded before anything else accesses it. Maybe I should rename this hook to be "ready" rather than "loaded"? Very cool!!
-
How do I save contact form submissions as pages?
ryan replied to Jim Yost's topic in API & Templates
The reason your snippet doesn't work is because you are trying to set the page's parent to be a string. You actually need to set the parent as a Page object, so you'd need to retrieve the page and then set it to be the parent. However, even if you did that correctly, you need to be concerned about the security of this. Make sure you define what are the allowed/valid parents. You don't want someone manipulating the posted values and then adding pages in the wrong place. I would suggest mapping your valid parents to numbers, which are simpler to validate from a post. You should never put user-submitted values into a selector unless you've validated/sanitized them first. Or better yet, map them like we do here: <?php $categories = array( 1 => '/form-results/', 2 => '/categories/something/', 3 => '/categories/something-else/', ); if($input->post->category) { // get the parent page (category) and save the form $n = (int) $input->post->category; if(isset($categories[$n])) $category = $categories[$n]; else $category = $categories[1]; // default $parent = $pages->get($category); // now that you have the parent, you can save the form like in previous examples } else { // draw the form and category selection echo "<select name='category'>"; foreach($categories as $n => $category) { echo "<option value='$n'>$category</option>"; } echo "</select>"; // print the rest of the form } -
That's a good point about .htaccess–I will make a point to mention that when moving this over to the install and/or requirements directions.
-
Adam, just sent you an email and attachment. I think sometimes my emails don't get through (maybe because of attachments), so just let me know if you don't get it.
-
Vanilla forums is originally what I wanted to use... and I tried to hard to get it working. I installed locally and seemed to encounter a lot of odd bugs. It worked, but it was not stable. Then downloaded another fresh copy and tried to install to the server, and couldn't even complete the install. Rather than spend days trying to get it working, I gave up on it. Granted it's code looks a lot cleaner than SMF's, but SMF "just worked" right out of the box. I probably need to tweak the theme more, but I'm reasonably happy with SMF so far.
-
This is really looking great. As for name, it sounds like you've got a lot of good options. I might suggest something that involves "overlay" just because that term is already familiar to many people with Drupal 7 using it. I also liked the "adminbar" term just because it's so simple and says exactly what it is (could also be something like "editbar"). Probably less technical sounding is better, because a term like "modal" means something to us, but it's far from common language (at least here). But regardless of what name you choose, this is shaping up to be a really cool module.
-
Most often the navigation structure of a site lines up pretty closely with your page structure. For example, your top navigation might consist of your root level of pages (those having the homepage "/" as their parent). And those in the sidebar might be under some section like /tools/. So the way you'd print you top nav would be like this: <?php $topnav = $pages->find("parent=/"); // OR $pages->get("/")->children(); foreach($topnav as $item) { echo "<li><a href='{$item->url}'>{$item->title}</a></li>"; } Then your subnav: <?php $subnav = $pages->get("/tools/")->children(); // OR $pages->find("parent=/tools/"); echo $subnav->render(); // OR output them manually like in $topnav But lets say that your site structure doesn't lend itself well to this. What you might want to do is add a checkbox field to your template so that you can mark what pages should appear in the nav. For instance, you might add a field called "toggle_topnav" and another called "toggle_subnav". When you edit the page, you would check the box for one or another if you wanted it to appear in the navigation. Then you'd get your $topnav and $subnav like this: <?php $topnav = $pages->find("toggle_topnav=1"); $subav = $pages->find("toggle_subnav=1"); And you'd output them using whatever manner you prefer (examples above). Lastly, don't be afraid to hard-code navigation markup. If it's something the client doesn't need to change, it is more efficient to hard-code it rather than generate it dynamically.
-
Best approach if I want to make some fields required?
ryan replied to martinluff's topic in API & Templates
This is the same situation as my last post. This was taken out last minute. Though I believe the implementation for this actually still is in place (just disabled). There are a few reasons for this. Changing a pages template to one that had required fields that didn't exist in the previous template resulted in lots of ugly error messages. Easy enough to fix, but I figured this was non-crucial and something I could get back to later. The other part was that I hadn't yet implemented the required option for some of the more complex fieldtypes, and wanted to put more time into it. Lastly, required fields drive me nuts (I have a strange workflow). But I recognize the need and you can count on this one coming back. -
Best approach if I want to set a default value for a field contents?
ryan replied to martinluff's topic in API & Templates
Adam is right that there is no default value yet. I agree it should be there, and it's in the Fieldtype module's interface to support it. But it's not implemented in the core. This is a side effect of this being a new project, and some non-crucial details like this are missing (though, this particular one used to be there). The way I usually handle defaults is from the template side. If something has an empty value, then (depending on the field and situation) I will output a default value. The advantage is that there aren't a lot of repeated default values in the DB. But that's too much of a compromise, so default values will be coming. This is one of those things I had in there for awhile, and then took it out to think about it and revisit it later.