Jump to content

johnstephens

Members
  • Posts

    103
  • Joined

  • Last visited

Everything posted by johnstephens

  1. $articles = database()->query(' SELECT * FROM textpattern WHERE section="blog" '); foreach($articles as $article) { $newArticle = new Page(); $newArticle->template = 'blog-post'; $newArticle->parent = pages()->get('/blog'); $newArticle->title = $article['Title']; $newArticle->body = $article['Body_html']; // Add other content from query to $newArticle->{fields} here $newArticle->save(); } I'm using something like the above to connect to a database, grab articles, and import them into ProcessWire. The source database has comments for these articles in a table called `txp_discuss`, which uses the `parentid` field to link comments to their parent article's `id`. I know I can associate that info with my `$article` items by editing the MySQL query, but I don't know how to pipe it in to a ProcessWire Comments field called `comments` that I added to the `blog-post` template I'm using. Does anyone have guidance to offer having done this before? Thank you in advance!
  2. Thank you, @adrian! I don't understand what the $np variable references. Is it the current ProcessWire page instance?
  3. Thanks! I feel very foggy on how to do that. Could you direct me to an appropriate code example? I'll look into that! I'm used to dealing with the DOM in JavaScript, but with PHP I'm not so savvy. DOMDocument looks like a great fit! Thank you!
  4. I'm working on a script for importing very old static HTML files into ProcessWire so they are searchable on the new site. What I have so far works, but I wonder if there are ways I can make the work of cleaning up the imported content easier, by doing more useful cleanup during the import. For this demo, suppose all the files exist in one directory, called "public", and suppose we're importing them all into the basic-page template. At this point, the basic-page template has been modified from the blank profile to include one additional textarea field called "body", which uses the CKEditor. <?php include './path/to/processwire/index.php'; // Use FileSystemIterator to save all the files in the 'public' directory // https://www.php.net/manual/en/class.filesystemiterator.php $files = new FileSystemIterator('./public'); // This is a callback function for the CallbackFilterIterator below $is_html_file = function($file) { return strpos($file->getFilename(), '.htm'); }; // Use CallbackFilterIterator to winnow the files down to only HTML files // https://www.php.net/manual/en/class.callbackfilteriterator.php $html_files = new CallbackFilterIterator($files, $is_html_file); // Input a regular expression and a string -> output an array of matches $preg_matches = function($regex, $string) { preg_match($regex, $string, $array); return $array; }; // Iterate over the directory objects stored in $html_files foreach($html_files as $file) { // Turn this file into a SplFileObject so we can read its contents // https://www.php.net/manual/en/class.splfileobject.php#splfileobject.constants.drop-new-line $_file = new SplFileObject($file); $contents = $_file->fread($_file->getSize()); $h1_content = $preg_matches('/\<h1\>(.*?)\<\/h1\>/i', $contents)[1] | false; // Create a new ProcessWire page and save the content into it $article = new \ProcessWire\Page(); $article->parent = $pages->get('/'); $article->template = 'basic-page'; $article->title = preg_match('/\<h1\>(.*?)\<\/h1\>/i', $contents) ? $preg_matches('/\<h1\>(.*?)\<\/h1\>/i', $contents)[1] : $preg_matches('/\<title\>(.*?)\<\/title\>/i', $contents)[1]; $article->body = $contents; $article->save(); } This successfully titles all the pages that have at least one h1 tag. (I know this is making a big assumption of proper markup, but it appears to be broadly correct in this one case.) The rest of the content is dumped into the page's body field. If this helps anyone else solve a similar problem, have the code! (WTFPL) But when one is dealing with archaic HTML using font tags and tables for layout (yeek!), this leaves much room for improvement. Something I'd like to do is get rid of all the layout tables and site furniture, like branding markup, navigation, and footer text. Of course, that is not marked up in a consistent way across all the documents. ? I wonder if anyone has guidance for something like this? Do you know of any best practices for automating the cleanup old HTML? Thank you! Edit: When searching for HTML tags, matches should be case insensitive (using the i flag after the delimiter). Also, use the content of the title element when there is no h1 tag on the page. This is all fixed in the code above.
  5. I have a site operating on Textpattern CMS. I still love Texptattern, which has served us well for many years—but the needs of this site have steadily grown beyond the limits of what Textpattern can do comfortably. I'd like to transition the site to ProcessWire, but the project doesn't have a budget for a full redesign right now. I'm wondering if it's feasible to install ProcessWire now, and create a limited number of pages managed by ProcessWire; then seamlessly redirect all requests for pages not managed by ProcessWire to Textpattern. Then, over time, I'd like to migrate the remaining templates and content to ProcessWire so that more and more of the site is being managed by ProcessWire, until the Textpattern site is no longer needed. Has anyone else done something like this? What steps did you take to minimize any possible disruption? How would you recommend setting something like this up? Thanks in advance for any guidance you can offer!
  6. That appears to have fixed the issue! I can log in after uncommenting the RewriteRule and the RewriteBase / line. Thank you! The installation was the latest stable version as of yesterday, ProcessWire 3.0.123. And the server runs PHP 7.2.17. I appreciate the help! Is there anything else I should look out for when running ProcessWire on a subdomain with this PHP version? Thank you again!
  7. I see the following message in my Apache error log file—I think it's associated with the attempts to load a page with the password reset code: [Mon Apr 29 11:10:13.761290 2019] [core:error] [pid 12889:tid 4057058112] [host opm.beardedbaby.net] [client 73.134.72.106:51354] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.
  8. Hi! I just created a fresh installation of ProcessWire on a subdomain. After commenting out the following rule in my .htaccess file, I was able to access the admin panel: RewriteRule ^(.*)$ index.php?it=$1 [L,QSA] But then I hit a dead end. When I attempt to log in, the same login screen reloads without any error display. I attempted to reset the password following the method here, but when I load the page with the password reset code, I get an error saying "Superuser has never logged in", and I still can't log in via the admin panel. I expect this is a known issue do to some quirk of my hosting that I never noticed before. Can anyone suggest steps I could take to troubleshoot this? Thank you!
  9. I'd like to understand this too. I too made sure that the editor role had user-admin permissions enabled, before coming across this problem.
  10. I can confirm that this fixed the problem for me, when I typed check_access=0 by hand into the selector field instead of copying and pasting it from Adrian's message. Thank you @adrian, for identifying the solution! And thank you, @Robin S for figuring out where I went wrong!
  11. Thank you, @adrian! That seems to cause ProcessWire to choke when loading a page in the editor. If I set the selector to this: template=user, roles=managing_editor It works perfectly for a superuser. The above selector throws no error when loading the editor using my managing_editor role—it simply doesn't populate the options. But if I set the selector to this: template=user, roles=managing_editor, check_access=0 ProcessWire throws this error, no matter what role I log in with: Unknown Selector operator: '' -- was your selector value properly escaped? Thanks again! I appreciate any insight someone might offer. Cheers!
  12. Hi! I created a page reference field linked to ProcessWire's "Users" page array. I do not have "create new pages" enabled for the field, and I don't want to use this field to create new users. When I am logged in as the superuser, it works as intended: I can select one or more registered users and associate them with the current page. But when I log in under a different role, the page reference field is empty, and does not allow me to select any users. I've checked the permissions for the role that I want to authorize to use the this field, and the permissions under the "Access" tab on the field itself. I don't see any role-based permissions I could add to the role that seem pertinent to this issue. Under the field's "Access" tab, I have added "View" and "Edit" privileges for the given role, but that does not seem to change the behavior at all. Can anyone offer some guidance in troubleshooting this? I know there must be some privileges I have not considered, but I don't know where to look. Thank you all in advance!
  13. Hi! I'm setting up a site search form and results page. Something I wanted to include in the search results was an excerpt of the content showing the highlighted search term plus some surrounding text—like the search_result_excerpt tag in Textpattern (function definition). I searched the forum and couldn't find an example of this. I suppose I'm just not hitting the right keywords relative to ProcessWire's API. Can anyone suggest examples or documentation that might help me with this? Thank you!
  14. I'm seeing this error and the site doesn't use jQuery at all on the front-end. It does have caching enabled on some templates (the type of caching offered natively by ProcessWire, not ProCache). Do I have to conditionally load ckeditor's jQuery on the front-end when edit-access users log in?
  15. You're wrapping up a design project for a new or substantially redesigned website. What are the not-so-obvious things on your list that you don't want to forget before you work on something else for a while? I realize that what counts as not-so-obvious in one project might be completely obvious and the first thing you do on another. Still, I'd love to hear what are the little things you do to tighten the screws and clean up before moving on to other work?
  16. So one of my requirements is that the shell script that must access the secret content is executed on the editor's personal machine, not on the server. After scouring ProcessWire's API, I think I've stumbled upon a way to do the main thing I use smd_access_keys for: function showSecretContent() { return "<pre>Secret content</pre>"; } /** * Generate a key unique to this user and for this day only. */ $segment = sha1($user->name . $datetime->date('Y-m-d') . $this->config->userAuthSalt); /** * Add the key to the URL for this page to use in links. */ $urlkey = $page->url . '?key=' . $segment; /** * If there is a key in the URL request, save it. */ $key = $input->get('key'); /** * To authenticate the key for certified users only, save all users to an array. */ $allusers = $users->find('id>0'); /** * If the current session is logged in to ProcessWire, show the URL key and the secret content. */ if (!$user->isGuest()) { echo '<pre><a href="' . $urlkey . '">'. $urlkey . '</a>' .'</pre>'; echo '<pre>This is what you will see:</pre>'; echo showSecretContent(); } else { /** * If the page is accessed without a ProcessWire login, check if the key is set. */ if (isset($key)) { /** * If the key is set, check all the users for a user who matches the key. */ foreach ($allusers as $thatuser) { if ( $thatuser !== $users->getGuestUser() && $key == sha1( $thatuser->name . $datetime->date('Y-m-d') . $this->config->userAuthSalt ) ) { /** * If there is a user who matches the key, who isn't a "guest", show the secret content, * But only if the session isn't logged in. */ if ($user->isGuest()) echo showSecretContent(); } } } else { /** * Don't show the secret content if the session isn't logged in or when the request doesn't include a key. */ echo '<pre>Go away</pre>'; } } Compared to smd_access_keys, this script does not allow keys to include custom expiration windows or expiration after a given number of requests—a "key" expires at midnight regardless of whether it was created at 00:01 or 11:59. This script also does not save keys to a database or allow keys to be voided in the ProcessWire admin. It also doesn't offer file protection. (smd_access_keys can be used to generate keyed links to download assets that you want to limit, like if you offer a MP3 or PDF download only to someone who signs up for your newsletter.) But I didn't need those features for this implementation. Can you see any problems with this approach? Are there security issues that I have overlooked? Are there any obvious improvements I should make? Thanks for any guidance you can offer! I appreciate your comments and support.
  17. Howdy, I have a document created and served by ProcessWire. I want to make it visible only to someone who is logged in to ProcessWire OR to an external shell script being run by an authorized user. I see how to designate authorized roles for access in the template's "Access" panel, and I see how to devise what content visitors can see in a page's template file using the API. Those cover the first scenario perfectly. However, if I exclude "guests" (or any other roles) from accessing a page via the template's Access menu or via the API, that also prevents someone from getting the URL via a shell script. I've solved this problem on other sites that use the Textpattern CMS, by adding the smd_access_keys plugin. The solution is twofold: The template or template section first detects whether an authentic user has privileges to view the page. If a privileged user is logged in, the template displays a special access URL that the user can use with their shell script. The URL access key has a built-in expiration, which prevents unauthorized access if someone somehow "guesses" the hash and salt at a later time. One of my uses for this feature is to allow a managing editor to generate documents that can be converted to PDF using Prince XML. I have also used it to provide temporary access to web forms and other content. I might use it to give someone temporary access to a file hosted on the site, as per the plugin's documentation. I'm not convinced that temporary access URLs is the only way or the best way to solve this problem, but I'm not sure how to do it at all using ProcessWire's API. Do you know a way to allow temporary access to resources managed by ProcessWire to designated external services (like a shell script), without giving access to anyone who goes to the URL? Thanks in advance!
  18. @cobrown Wouldn't that include all architecture that uses right angles?

  19. Thank you, ottogal and Zeka! The Autocomplete field + allowing page creation is exactly what I needed. Perfect!
  20. Hey, Intelligentsia! I have an unusual data modeling need, and I'm not sure how to approach it. I'm creating a ProcessWire template to track Events (after the fact). When creating an event, the user will be able to select a category (Page reference field), start and end times (datepickers), etc. One datum we need to track is the number of participants. - Some participants may have been enrolled previously, so I could have a multi-select page reference field linking to the individual participants. - But some of the participants will not be enrolled, so I'm thinking I need a numeric entry for those who are not enrolled. When I report on participant counts for each event, it would be fine to add the number of registered participants to the numeric entry of unregistered participants to form a total. But when I report on participant counts for a series of events, that won't work. Suppose Event 1 has 25 participants, and event 2 has 26 participants (one participant is new, and the other 25 attended the first event). I create a report that counts participants for both events. I want the report to show 26 participants total—not 51. I'm not sure how to model this. If *every* participant was registered in some way, we could just log unique persons via a mult-select page reference field. But creating a unique identifier for every participant—especially for participants who attend only one event—would be an overwhelming burden for the client. On the other hand, plain numeric entries seem wholly inadequate. I'm not sure how to normalize or finesse the data model to get the information I need without creating an unmanageable data entry chore. Any ideas?
  21. This is a story of desire, stilted romance, triumph, and discovery of a simpler way. TL;DR: ProcessWire has hidden goodies in it, but it makes me wonder what else I'm missing. The other day I was thinking, "Hey, I make a lot of navigation. Lists of links, glossary-style HTML sitemaps with links and descriptions, nested menus, etc. I wonder if I could automate some of the markup and stuff by making a custom function?" So normally, I might build a navigation menu in ProcessWire like this: $menu = $pages->get('/')->children; $out = "<ul class='my-menu-class'>"; foreach($menu as $item) { $out .= "<li class='my-item-class'><a class='my-link-class' href='{$item->url}'>{$item->title}</a></li>"; } $out .= "</ul>"; Within this, there are only a handful of things I might want to handle differently for different menus: The classes, the HTML elements, the pages included, the sort, and the limit, for a few. After some amateur writing and testing, I came up with a new object class that would take some config and return a menu in HTML: $out = ''; require_once('./ClassMenu.php'); $menu_settings = array( 'pages' => $pages->get('/')->children, 'label_class' => 'my-label-class', 'label_tag' => 'dt', 'desc_class' => 'my-description-class', 'desc_tag' => 'dd', 'desc_field' => 'description', 'wrap_class' => 'my-list-class', 'wrap_tag' => 'dl' ); $menuzilla = new Menu($menu_settings); $out .= $menuzilla->render(); The above menu would return something like this: <dl class='my-list-class'> <dt class='my-label-class'><a href='/about/'>About</a></dt> <dd class='my-description-class'>About page description…</dd> <dt class='my-label-class'><a href='/products/'>Products</a></dt> <dd class='my-description-class'>Product page description…</dd> <dt class='my-label-class'><a href='/misc/'>Miscellanium</a></dt> <dd class='my-description-class'>Miscellanium page description…</dd> <!-- etc. --> </dl> It worked great, and it was pretty flexible. As I considered adding more complex functionality, I remembered Soma's tutorial about building forms with ProcessWire, and I wondered if I could learn something about building markup from ProcessWire's render method. That's when I found MarkupPageArray. MarkupPageArray, for those who don't know, adds a render method to page arrays, giving them the ability to generate HTML without further ado, like so: $out = ''; $menu = $pages->get('/')->children; $out .= $menu->render(); But what if you don't like ProcessWire's default markup or classes? You can override that simply: $out = ''; $menu = $pages->get('/')->children; $out .= $menu->render(array( 'listMarkup' => "<dl class='my-list-class'>{out}</dl>", 'itemMarkup' => "<dt class='my-label-class'><a href='{url}'>{title}</a></dt><dd class='my-description-class'>{description}</dd>" )); This renders the same HTML I posted above, using only ProcessWire's own API and no custom functions. It brings a tear to the eye. So my question is this: What other common site components are hidden in the API waiting to be uncovered by the hapless and meek in their thrashing about for wisdom?
  22. Concepts like "bootstrap script" and writing my own importer were a bit much for me when I initially posted this question, but now that I'm facing a real need I might have to wrap my mind around them. At the time I wrote, I didn't have a specific site in mind to convert, but now I do. It's one of my more complex Textpattern sites, but it's not insane. Here's what I have: - 7 major content "sections", some of which would be nested in a ProcessWire site. I also have some utility sections used for things like search and site map. - 660 "articles", most of which belong to a knowledge base. - 39 categories in two major divisions. Each article in the knowledge base belongs to two categories. - The site uses Textpattern's MLP plugin/hack to offer content in two languages. The 660 article IDs include the renditions in both languages. On the front end, that means you can click on a language link for any article and view it's rendition in the other language. The URL changes from domain.tld/en/{section}/{url-title} domain.tld/es/{section}/{url-title}. - The site uses 15 custom fields, with the glz_custom_fields plugin offering support for different field types. Articles in different sections use different combinations of the custom fields, but all the custom field data lives in the same table with the articles. I think that covers all the complicating factors. In a vanilla Textpattern installation, all the article content lives in one table, and I suppose importing it would involve some way of mapping the columns of that table to pages and fields in ProcessWire, with the "section" field designating the rootParent page. But this site has the added convolution of a localization table that maps articles in both languages so they can be linked appropriately. Tom, when you say you transferred the content manually, do you mean you opened the article in Textpattern's editor (the Write tab) and simply copied and pasted each field to ProcessWire? Is that the method you'd recommend for this site, or is there some way of automating it that you'd suggest? Thanks in advance for any guidance you can offer!
  23. The thing I love about my Ghost blogs is the writing UI: Write Markdown on the left, and it appears fully rendered on the right. It also has the coolest while-you-write image upload utility I've ever seen. That's not covered by pw-ghost, right? Not having Ghost's writing UI isn't a deal-breaker, and I can't wait to try this out. If I could replace my Ghost blogs with a ProcessWire equivalent, I'd love to forgo the chore of maintaining a Node server!
  24. @jbswetnam MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODk=

×
×
  • Create New...