Jump to content

ryan

Administrators
  • Posts

    16,714
  • Joined

  • Last visited

  • Days Won

    1,515

Everything posted by ryan

  1. I still think this strategy would be fragile over time. It's not the job of a web service to keep track of deleted things, that's the job of the client accessing the service. I don't know of any major web service that attempts to bundle deleted items in a feed. The problems arise when something really does get deleted, and your web service has no record of it. If the client accessing your service expects you to keep track of all deleted items, then this will break sooner or later. You can't count on everything being in the trash. That is just a temporary container. Sooner or later it has to be deleted. Not to mention, showing deleted items in the feed means your feed has unnecessary overhead. The responsibility has to rest on the client side or else the system will break down at some point. Here's what I usually do on the client side: Keep two extra fields per record in the feed: import_id (integer) and import_time (unix timestamp). The import_id reflects the unique ID of each item from the feed, and is needed to connect items from the feed with your local items. The import_time reflects the timestamp of when the item was last updated. Before the feed is pulled, the client records the current timestamp and keeps it somewhere, like in a variable: $startTime = time(); Then they pull the feed, iterating through each item in the feed and updating or adding an associated page item for each. Note that the import_time of each page item is updated regardless of whether any changes occurred. You are essentially identifying all the items that are still active by updating their import_time field. $parent = $pages->get("/path/to/items/"); $http = new WireHttp(); $feed = $http->getJSON('http://domain.com/path/to/feed'); if(is_array($feed) && count($feed)) { foreach($feed as $data) { $item = $pages->get("import_id=$data[id]"); if(!$item->id) { // add new item $item = new Page(); $item->parent = $parent; $item->template = 'some-template'; $item->import_id = $data['id']; $item->save(); } else { $item->of(false); } // populate item $item->title = $data['title']; // ... // update import_time $item->import_time = time(); $item->save(); } // after importing the feed is complete, find the pages that were NOT updated // these pages may be deleted or trashed foreach($parent->children("import_time<$startTime") as $item) { $item->trash(); } } I like if the web service also provides an "exists" function, to determine that an item really has been deleted. This returns a thumbs up or down for each item requested, as to whether it exists or not. It's not entirely necessary, but it adds a little extra peace of mind as a backup in case something goes wrong. This is described in one of my posts above. Combined with the code above, the part that does the $item->trash() would just be preceded by a call to the exists feed to reduce the list of $items to only those that are confirmed to be deleted: // find which pages weren't updated $ids = array(); foreach($parent->children("import_time<$startTime") as $item) { $ids[] = $item->import_id; } // send the IDs of those pages back to the feed, asking it to confirm deletion $feed = $http->getJSON('http://domain.com/path/to/feed/exists?id=' . implode(',', $ids)); foreach($feed as $id => $exists) { if($exists === false) { // deletion confirmed, so page may be deleted $item = $pages->get((int) $id); $item->trash(); } } As for memory issues: any feed dealing with a lot of data needs to provide pagination of feed data (example), otherwise it will not be able to scale and will break if there is too much data. Pagination of a feed is a lot simpler than you'd expect for both the service and the consumer. It not much more than wrapping everything in a foreach.
  2. Make sure that you are putting that code somewhere that gets executed on every request, and before any code that needs to use it. Also, attaching the hook to $pages may be the problem in your case, because Pages is different from Page in terms of the underlying PHP classes. A better approach would be this: wire()->addHookAfter('Page::path', null, 'hookPagePath');
  3. Is it possible your browser doesn't have the necessary plugins to display videos (Flash, etc.) or has some kind of blocker installed? I know with my copy of Firefox, both are the case (no flash, and everything external blocked... but it was intentionally configured that way).
  4. If you rename the template, then you'd also have to rename the associated language file, otherwise they'd be disconnected from each other.
  5. It does grab the version automatically, though only once per day (and it caches it). So it may take up to a day at the most in order to display the updated version number.
  6. I love the idea of getting TinyMCE out of the core and as a module that's installed separately. But the reality is that the marketing folks wouldn't let us do it. They love to be able to say "includes rich text editing!". Lots of people look for this in the features list when evaluating a product and don't bother proceeding if it doesn't include a rich text editor. When people are evaluating products, they don't necessarily know about the module ecosystem and how easily things can be added. Just recently I read someone slamming Drupal because it didn't come with a rich text editor. Obviously that's a silly statement, but perceptions like this matter in growing a product. The other side of it is that providing a really good rich text editing experience means integrating the editor into the system more tightly than most other modules. For instance, our rich text editors (TinyMCE and CKEditor) are integrated into the image and link selection modules. Making sure this all stays working smoothly dictates that it's best kept in the core and upgraded with the core. By this token, I'd like to have CKEditor integrated into the core too, but bundling two rich text editors is probably just too much. As for upgrading to TinyMCE 4, I'd like to do this soon. I had tried to already in the past, and ran into all sorts of issues (they have changed a lot, but it was in beta when I tried it), but this will happen sooner or later. Because of the significant differences, we may have to produce TinyMCE4 as a separate module... or rather keep TinyMCE3 active as a separate non-core module, if we replace the core with TinyMCE 4. That's because the two aren't totally compatible with each other, and anyone that's doing anything complex with their MCE installation may be dependent upon version 3 already. Because switching to TinyMCE 4 is nearly a different rich text editor, we may consider replacing it with CKEditor for our core rich text editor, given that this module is already pretty much in a stable state. Whether the core includes TinyMCE 4 or CKEditor 4, people will have to reconfigure their editors after the upgrade either way. I hate creating upgrade hassles for people, so maybe the best thing to do is just keep TinyMCE 3 where it is in the core and develop other rich text solutions separately. I don't really know, just a whole lot of factors and compromises no matter which direction we go.
  7. As for how to make the ProcessPageEditImageSelect module accessible from the front-end, you'd need to open it in a modal window from your editor. Both our TinyMCE and CKEditor modules are already configured to do this, using jQuery UI modals. Note the properties passed to the ProcessPageEditImageSelect window (as GET variables) from TinyMCE or CKEditor in the admin. You'd need to duplicate those properties in your own usage (if not already using TinyMCE or CKE). Without looking at the code here, I believe it includes things like the page ID and image selection (if one already selected). Note that the ImageSelect module does nothing more than populate some fields in its own screen. You'll see these fields on the screen you see after selecting an image: filename, width, height, and whether it's linked to larger version. It's the TinyMCE or CKEditor plugins that do the rest of the work, pulling those values out of that modal window (with javascript) and inserting them into the editor. So those plugins are also good to look at, and I'd imagine they could be used on the front-end like they are used on the back-end. The ProcessPageEditLink module works exactly the same way.
  8. One value of a NullPage is in cases where you have output code. Lets say you are outputting a table of skyscrapers, with the columns of city, architect, architectural_style and floors. Lets also say that city, architect and architectural_style are single page references. If we are allowing a NullPage for these single page references, our output code can look like this: foreach($skyscrapers as $item) { echo " <tr> <td>$item->title</td> <td>{$item->architect->title}</td> <td>{$item->city->title}</td> <td>{$item->architectural_style->title}</td> <td>$item->floors</td> </tr> "; } The code above doesn't have to examine whether each property is present or not before outputting it. If we instead chose the option of Page or NULL (rather than NullPage), then we'd have to check every single one of those page references before outputting them, or we'd get a PHP error. So it would make our output code more complex: foreach($skyscrapers as $item) { echo "<tr>"; echo "<td>$item->title</td>"; if($item->architect) echo "<td>" . $item->architect->title . "</td>"; else echo "<td></td>"; if($item->city) echo "<td>" . $item->city->title . "</td>"; else echo "<td></td>"; if($item->architectural_style) echo "<td>" . $item->architectural_style->title . "</td>"; else echo "<td></td>"; echo "<td>$item->floors</td>"; echo "</tr>"; } The above is certainly just fine too. But if you are using a NullPage, your code will always be dealing with the same derived type (a Page) as opposed to two types (Page and NULL). This can make your code simpler in some situations, and make your site less fragile. But there's also a case to be made for NULL, as you may actually want to account for every unset page reference and want an error when your code isn't handling it. That's why I thought it was best to let people decide what worked best for them in this case.
  9. I just posted an update to the admin theme that resolves the mobile responsive views, or any view that causes menu items to split on two lines. If the topnav menu items take up enough space to need two lines, it collapses them down to a hamburger: Likewise, if there are enough tabs in the page editor (or anywhere else) for them to wrap on two lines, it converts them from tabs to a more stable button appearance: I've removed that "home" icon, and the "View Site" link now now contained in the Tools menu. However, when you hover the tools menu, it also becomes a View Site link, so you don't have to go through the menu to get to it unless you want to. Lastly, I've upgraded the entire system to FontAwesome 4 (and the associated fa-* classes rather than icon-* classes). Unfortunately they've change a lot of the icon names in FontAwesome 4, so it required a little more work than I expected. If you are using any icons for your templates or fields, it's possible they may have renamed ones you are using, so if you have any icons that stop working, you may need to update the icon name (reference). Most icons carry the same names and won't require any update though. @Manfred62 Hold off on translating files just yet, as I'm still moving stuff around and will try and get the multi-language translation part simplified as much as possible. But I've not gotten to that part yet.
  10. This admin theme has been converted over to be an AdminTheme module (a new module type just added to the PW core). If you grab the latest dev, you can now delete your /site/templates-admin/, as it will switch to using this admin theme as a module. The color theme is now selectable from the module settings screen. It will automatically install when you upgrade to the latest dev and login to your admin. But if you want to revert back to another admin theme in /site/templates-admin/ (or even the old admin theme in /wire/templates-admin/), then all you need to do is uninstall the AdminThemeDefault module. There are several advantages to having admin themes be modules. I'll get into those later, but one is that you can have multiple admin themes installed. When you've got more than 1 installed, the User Profile screen will let each user select which admin theme they want. It's easy to convert any existing admin theme to a module: 1. Move all the admin theme files to /site/modules/AdminThemeSomething/ - replacing "Something" with whatever the name of the admin theme is. 2. Create a file in that directory called AdminThemeSomething.module (again, replacing "Something") and place the following in it (substituting for your own info): <?php class AdminThemeSomething extends AdminTheme implements Module { public static function getModuleInfo() { return array( 'title' => 'Some Admin Theme', 'version' => 1, 'summary' => "Enjoy this nice admin theme", 'autoload' => "template=admin" ); } } 3. Install the module from your admin, the same way you would install any other module. That's it! If you have the dev admin theme (or another) installed in /site/templates-admin/ and no longer need it, feel free to remove that directory. But don't remove the one in /wire/templates-admin/.
  11. Lpa: I looked at your map, and you've got a Javscript error occuring related to magnificPopup. That error is occurring before the map resize gets to occur (due to the setTimeout), so it is effectively preventing the JS code from running. If you can resolve the JS error you are getting, my guess is it would start working.
  12. In that case, it can be done. The security aspect was really the main thing that would prevent one from wanting to use this on the front-end, as it's a module meant for people that have access to edit pages. Not that I've tried it, but if the security aspect is not applicable then it should be possible to get it to work on the front-end.
  13. The submit buttons that sometimes didn't register were driving me nuts. After trying to debug it for a long time, I gave up and just dumped the jQuery UI theme that we were using, downloaded a fresh copy of jQuery UI and put in it's default UI theme to replace. That seems to have fixed the issue. The only place you'll likely notice the jQuery UI theme change is in the datepicker, which isn't quite as pretty as the one that came with that other theme... but I'd had it with the submit button issue. I think we're better off just to stick with the latest version of a generic jQuery UI theme from the source, just to avoid bugs like the submit button one. It doesn't matter much to the admin theme, as very few things are actually styled by jQuery UI's stylesheet (datepicker, slider, etc.) Joss–glad to see you experimenting! Regarding a "site" link, the theme has one as the home icon that leads the breadcrumb trail. I like it and think it makes sense, but have heard more than once that others aren't getting it, so I think we'll have to add a site link somewhere else. Regarding opening in a new window, this would be controversial because a lot in the development community think that a site/app should never open a new window and instead leave the choice to the user (click vs. cmd-click). I lean towards the side of leaving it to the user... not because I have a problem with target=_blank, but because it would drive me insane to have new windows opening all the time like that.
  14. It could potentially be done right after we wrap up the 2.4 release. The amount of work and time involved would probably go a little beyond what was involved in developing dependencies, but not not nearly as time intensive as developing the multi-language support. The workflow would simple and the same thing as it is when you are editing an unpublished page at present, at least from the perspective of the editor. I think this is also similar to what Soma was talking about with their existing system. Because it would be a draft of a live page, there would also need to be an option to "abandon draft". But beyond that, it would be the same as editing an unpublished page. Of course, you could preview it and such, like you can with an unpublished page. Drafts would use more overhead than a regular page edit (since it has to make a copy of the page first), so you'd designate which templates you wanted it to utilize the drafts system for. The page creation process would not change at all, since a draft would only come into play with a published page (a draft is a working copy of a published page).
  15. By parent, do you mean the page that the repeater lives on, or the parent of the page that the repeater lives on? If parent is the page that the repeater lives on, then there'd be no reason to query it since you could just access $page->repeaterField->find(). So I'm assuming you mean parent of the page that the repeater lives on. That's a tougher question, and not sure there is a good answer since repeaters are disconnected from the site's structure (they have their own structure off in the admin). The only physical connection between the repeater items and the page they represent is the repeater page name, which uses the syntax "for-page-[id]". Currently I can't think of a straightforward way to perform that query, at least in a manner that would be scalable to tens of thousands of items. A non-scalable way to do it would be: $selector = "template=repeater_eventdetails, date>0, sort=date, include=all, name="; $parent = $pages->get('/some/parent/'); foreach($parent->children as $p) $selector .= "for-page-{$p->id}|"; $items = $pages->find(rtrim($selector, '|'));
  16. I haven't, but am also interested in hearing if anyone else has experimented with this.
  17. ryan

    I'm back

    Soma you and some other guys here make my job easy because most of the hard questions were already answered. So thank you for all the help and support give here. You all make this forum great.
  18. I've actually been putting some thought to drafts and think I've got some good ideas to move forward with a simple drafts system soon. Page status 4096 is reserved for Page::statusDraft. That keeps drafts in the Unpublished territory, excluded from all but "include=all" selectors. Page IDs 500-999 are reserved for drafts (so the IDs will be reusable, unlike other page IDs). I don't imagine many would need more than 500 drafts at a time, but if they do, we can make the ID reuse optional. Drafts will be an optional module, not installed by default (like autocomplete, repeaters, etc.) Versioning will be something different entirely, as I don't want versions filling up the pages tables. But versioning will likely use drafts as a transition point.
  19. ryan

    I'm back

    I finally caught up with those 9 pages of posts and am down to 0 posts in my "new content" list. Sometimes things escape my new content list, so if anyone is waiting on me for anything or aware of something that I might have missed, please let me know. Happy New Year to everyone! Hope that you all have a great New Years day today.
  20. Here is the same thing as a Textformatter module. To use, paste the following into /site/modules/TextformatterImagesToPages.module. Then, in your admin, to to Modules > Check for new modules. Click install on the new module. Then go to Setup > Fields and edit the field you want it to operate on ('body' for example). On the "details" tab, choose the "Images to Pages" text formatter, and save. The text formatter will be applied automatically when you access any $page->body field from the front-end of your site. /site/modules/TextformatterImagesToPages.module <?php class TextformatterImagesToPages extends Textformatter { public static function getModuleInfo() { return array( 'title' => 'Images to Pages', 'version' => 1, 'summary' => "Takes image links in HTML and converts them to page links." ); } public function format(&$str) { $rx = '{href="' . wire('config')->urls->root . 'site/assets/files/(\d+)/.+?"}i'; if(!preg_match_all($rx, $str, $matches)) return; foreach($matches[1] as $key => $id) { $page = wire('pages')->get((int) $id); if($page->viewable()) $str = str_replace($matches[0][$key], "href='$page->url'", $str); } } }
  21. Most of the instances I've heard of where people lost their work had to do with creating new pages that weren't yet published. For unpublished pages, the issue Pete mentioned is really not an issue at all. Perhaps a setting to limit the auto-saving to unpublished pages would be all that's needed, since that is the time when this module is likely most valuable. I'd suggest this might even be a good default setting. The 3 instances I'm aware of–Mike (cmscritic), Teppo, and Peter–would have all been for an unpublished page (I think) and would have been saved by this module. Nice job btw!
  22. First question would be why not just link to the page rather than the image in the first place? But thinking on it more, I'm betting you are inserting these from TinyMCE/CKEditor? Something like this might work: function replaceImagesWithPages($str) { $rx = '{href="' . wire('config')->urls->root . 'site/assets/files/(\d+)/.+?"}i'; if(!preg_match_all($rx, $str, $matches)) return $str; foreach($matches[1] as $key => $id) { $page = wire('pages')->get((int) $id); if($page->viewable()) { $str = str_replace($matches[0][$key], "href='$page->url'", $str); } } return $str; } echo replaceImagesWithPages($page->body); This type of thing could be bundled into your own Textformatter module for easy re-usability (let me know if I can elaborate). Note that this was written in the browser and not actually tested, so may likely need adjustments.
  23. Strangely I can't duplicate the issue, but the error message you guys have mentioned is correct. It was attempting to call a parent::__construct() even though the class it was extending had no __construct in the chain of classes. I'm guessing some versions of PHP care and some don't. I have updated the module to remove that call. Please let me know if it works for you guys now?
  24. In your /site/config.php file, you'll want to set a whitelist of allowed hosts. This was just added in 2.3.9 and it's a recommended security optimization. Include both your SSL and non-SSL host names in there. Please let me know if this resolves the issue you are experiencing?
×
×
  • Create New...