Search the Community
Showing results for 'runtime'.
-
I often thought about the ability to provide an easy way to install themes like in e.g. Wordpress. Reading the docs about template files, an idea came to me, how this might be possible: Use another layer between template, template files und the connection to the fields. The workflow would be: Declare the use of fields in the template file with their type When creating the template in admin, the declared fields are read in and the user could connect it to his own fields ProcessWire must handle the cloning of the reusable fields on runtime Does this sound serious to anyone? Or do you think this is not possible, achievable or desirable? I know it adds kind of a templating language and another level of abstraction, but for the goal of easy templating this may be not a major drawback, thinking of small business websites or blogs. Using this for reusable template themes, would bring a lot more attention from less php-skilled user and help spread ProcessWire in general. Designers could built their themes and an average user could use that to built his own site.
-
I understand the convenience, but I don't think having separate core downloads for PW depending on language (or other factors) is sustainable from a version or security standpoint, so would rather avoid that by always having the core download be directly from our GitHub. But having clear additional download links for language pack, docs, etc., to accompany the main download link would be great. Longer term, we'll have an automated download function that can bundle modules and language packages automatically at runtime.
-
Enhanced debug mode The dev branch also includes an enhanced debug mode in the admin. It's all been cleaned up to look better and provide more info. It also includes a new section that outlines all of the runtime attached hooks currently active in the system:
- 143 replies
-
- 11
-
-
You have to consider XSS anywhere that the [untrusted] user can enter content that will be saved and echoed back to other users. In a default ProcessWire installation with no 3rd party modules, there aren't many places to consider this. The admin is designed for trusted users only. The comments module is the only thing that accepts and stores untrusted user input. It won't store markup in comments. It filters by stripping markup before storing a comment, and by entity encoding output when displaying a comment. Also consider that ProcessWire is designed to let you do what you want to do. If you want to create a text/textarea field that lets you enter script tags, then ProcessWire won't stop you. There are plenty of legitimate uses for such things. There simply isn't anywhere that an untrusted user can enter markup. But HTMLPurifier might have a place in your individual installation. If you are dealing with untrusted user input and allowing markup (HTML) then HTMLPurifier is essential. We have an HTMLPurifier module, ready to use. Content is validated and sanitized server-side, not client side. Though on text-based fields entered in the admin, we make no assumptions about what you are using them for. Even scripts are a legitimate use case (consider video embedding, etc.). Some Inputfields may have client-side validation built-in to do one thing or another (TinyMCE is an example). Though ProcessWire doesn't care about client side validation. What sanitization/validation takes place depends entirely upon the Fieldtype. From the admin side, the text-based fieldtypes are designed to contain any text, including any kind of markup. This is one reason why ProcessWire's admin is focused on trusted users only. While I don't recommend using PW's admin with untrusted users, there may be situations where you have "mostly" trusted users. In such cases, you may want to avoid things like rich text fields and focus on LML-based text formatters (safe ones, like Textile Restricted or BBCode) or even HTML Purifier. Our new CKEditor module actually does use the HTML Purifier for inline mode, since inline mode markup is not entity-encoded when presented in the admin. This is a consideration for any text fields that you don't want to support markup. Such fields might include your 'title' and 'summary' fields, but would exclude something like your 'body' field, which is designed specifically to contain markup. ProcessWire uses a type of modules called Textformatters. For any field that you want to have runtime output formatting, you can add an HTML Entities (or other types) Textformatter from Setup > Fields > [your field] > Details. This is only applicable to text-based fields. For entity encoding output yourself, use $sanitizer->entities($str). Generally you wouldn't ever use this on your managed content stored in PW. Instead, you would use it when echoing back something that the user entered, like search query: $q = $input->get->q; if($q) { $results = $pages->find("body%=" . $sanitizer->selectorValue($q)); echo "<h2>You searched for: " . $sanitizer->entities($q) . "</h2>"; if(count($results)) echo $results->render(); else echo "<p>Sorry, no results were found.</p>"; } ProcessWire's API does not use SQL queries, unless you choose to use the $db variable. So if you want to execute SQL queries, then you should follow best practices towards avoiding SQL injection. However, there are very few scenarios where any of us use SQL in developing websites or apps in ProcessWire, so it's generally a non-issue. What you do have to consider instead is selector injection, even if it's not nearly as potentially dangerous as SQL injection. So that's why we have the $sanitizer->selectorValue(); function. Note the use of it in the code segment above. Yes, all ProcessWire forms are CSRF protected.
-
Since the password has to be retained to send to the service at runtime, there's not much point in trying to hash it. And if you encrypt it, who are you ultimately trying to prevent from seeing it? I suppose it depends on what the password is ultimately used for. But I don't think you should try to over think it too much because we're talking about one password for [presumably] a non-critical service… not a database of passwords for multiple users that are likely spread out over multiple services. The problems from storing passwords in plain text or loosely hashed become real when you are dealing with user accounts at some scale beyond yourself. But in your case, in order for someone to get to that single password, they will have had to already compromised the system and broken into the database. So long as you aren't building a banking application or something high security, I think it's reasonable to just store the single password in the module config? After all, the database password itself is ultimately in plain text on all web servers too. But it is secure enough for all of us to trust our sites to. If you find that the password you need to store really is something that needs more security than the database itself, let me know and I may be able to suggest a couple things. As for the inputfield, try using InputfieldText with ->attr('type', 'password'); rather than InputfieldPassword. InputfieldPassword assumes that the password is not reversible, so it doesn't attempt to re-populate the field with it.
-
I think that file/image/page URLs in textareas are the only place where the issue can occur. It happens just because editors like TinyMCE are storing ready-to-output HTML. Everywhere else in ProcessWire (except cache files), everything is stored as references, so it doesn't matter where the site is running. Our eventual built-in solution will just ensure that any local URLs in textareas are always saved as if the site were running from root (via the sleepValue function), and prepend any subdirs to root paths at runtime (via the wakeupValue function).
-
With just a little more work you could put that function into a module that runs on page save which would then be ideal for your needs For example, something like this: <?php class SortPageTitles extends WireData implements Module { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them * * @return array * */ public static function getModuleInfo() { return array( // The module's title, typically a little more descriptive than the class name 'title' => 'Sort Page Titles', // version: major, minor, revision, i.e. 100 = 1.0.0 'version' => 100, // summary is brief description of what this module is 'summary' => 'Sorts pages by title by second word if first word is "a, the, an" etc.', // Optional URL to more information about the module 'href' => 'http://processwire.com/talk/topic/3306-sorting-by-page-title-with-some-complexity/', // singular=true: indicates that only one instance of the module is allowed. // This is usually what you want for modules that attach hooks. 'singular' => true, // autoload=true: indicates the module should be started with ProcessWire. // This is necessary for any modules that attach runtime hooks, otherwise those // hooks won't get attached unless some other code calls the module on it's own. // Note that autoload modules are almost always also 'singular' (seen above). 'autoload' => true, ); } /** * Initialize the module * * ProcessWire calls this when the module is loaded. For 'autoload' modules, this will be called * when ProcessWire's API is ready. As a result, this is a good place to attach hooks. * */ public function init() { // add a hook after the $pages->save, to sort certain pages if necessary $this->pages->addHookAfter('save', $this, 'sorttitles'); } /** * Hooks into the pages->save method and sorts pages with a certain template based on certain criteria * */ public function sorttitles($event) { $page = $event->arguments[0]; // Only run if the page we just saved has the "item" template if ($page->template == 'item') { $items = wire('pages')->find('template=item'); foreach ($items as $item) { if ($item->sort_title=="") { $item->setOutputFormatting(false); $new_title = (preg_match( "/^(the|a|an)\s/i", $item->title)) ? substr(strstr($item->title," "), 1) : $item->title; $item->sort_title = $new_title; $item->save(); } } } } } I've not tested it, but it's based on the helloworld module, so if you download it (attached) and put it in your /site/modules folder it should work for you, and hopefully be a good example of how easy it is to write modules too SortPageTitles.module
-
"Template" inPW is like a db table with all its fields. If you change the structure of the table you loose data... Except for fields that are the same in the template you change to. Changing a template of a page is not something you do all the time and only to give this page a different content or meaning. It's all about data here and not presentation. "Template File" is more of what you think of in MODX I think, and it's linked to the Template by it's filename. But you don't have to create a file for a template unless you want or need to have it renderable or viewable. The Template name also can be changed, thus mapping it to a different template file. As WillyC pointed out you can also leave the name (which often is used in template code as indentifier and you don't want to change that, unless developing) and define a alternative name that will be used to map to a template file. You can even have all your template share the same template file simply by setting this. A page using a template to build it's data model also isn't locked in to a certain presentation. You can change your presentation template on runtime or have some MVC like approach even. You could even build a simple system to be able to select from different "presentation modes" on a page using page field to build a selection. You make it up. There's many possibilities with how Template and Template Files work together and it allows for some great flexibility in a simple manner. I even after some time working with PW I found ways I didn't see or knew it was possible before, so enjoy the ride. Not sure this is of any help. Always struggle a little explaining this stuff.
-
Is 'timestamp' a field in your system, or one that you are assigning purely for runtime use?
-
Moving PW is creating problems with TinyMCE images
ryan replied to dreerr's topic in General Support
Does it swap in the actual directory at runtime? Meaning, if you click the "HTML" button in TinyMCE, do you see the correct URL rather than the relative one? Ultimately though, if they are storing a directory that is a placeholder it's the same problem in that the content lacks portability. But it at least sounds more portable. Handling directories for files/images is one thing, because the URL has common elements for all of them. But handling URLs to other pages is another matter. -
populate global field with content from other fields
Soma replied to dreerr's topic in Getting Started
Welcome. Well I can't sleep since two years because of PW There's a concencate module by Ryan that would allow to combine fields to a runtime value. You can open title field and make in unglobal and remove it from the template you don't need. PW requires only a "name" which is like an id. Have fun and sleep well! -
Easiest Way to Include Assets in a top-level directory
Wanze replied to ruzel's topic in Getting Started
Or: $config->urls->assets For other runtime config urls: http://processwire.com/api/variables/config/- 2 replies
-
- 1
-
-
- codeigniter
- structure
-
(and 3 more)
Tagged with:
-
But then how to get the dynamic description into the static HTML from TinyMCE? It would have to be done at runtime with some kind of variable replacement. That's something I've been trying to avoid with TinyMCE, though don't have good enough reasons. One of my problems with the default dialogs in TinyMCE (and most other editors, for that matter) is that they present every possible option and only a developer would understand the vast majority of them. For instance, the distinction between a description and a title is going to confuse the non web-developers. I tend to think we should just make the behavior a checkbox in the module's configuration rather than the image editor. Another thing to consider is that we'd be duplicating an 'alt' to a 'title' for the benefit of javascript. When faced with that, it's technically more efficient to duplicate the attribute in javascript rather than in the markup, i.e. $(document).ready(function() { $(".gallery a > img").each(function() { // copy image 'alt' attribute to a 'title' attribute $(this).parent('a').attr('title', $(this).attr('alt')); } }); Do you mean add attributes? Not possible at present, just because our JS-side of things needs to know what the attributes are ahead of time (though some re-coding could solve that). But since these tags only accept a limited number of attributes anyway, I'm not sure we need that scalability here.
-
It is supported if you allow it in your field's TinyMCE settings. Setup > Fields > body > input > Advanced TinyMCE settings. It's not recommended though, because being able to enter things like iframes or scripts directly in body copy are considered bad security. It's better to use a runtime module like the TextformatterVideoEmbed module. If you are finding that doesn't work in your case, then give more details -- what is the URL you are pasting in? The first thing we'd want to do is make sure it is in the expected format. Next we'd make sure it is in it's own paragraph (i.e. has 1 blank line above and below it, if placed among other copy).
-
Make a magazine style homepage for Blog profile?
ryan replied to siulynot's topic in Getting Started
This is in fact simple to do, but it does require knowing how to put it together without a CMS first. So I would suggest getting your layout up and running as a functional mockup, outside of ProcessWire or any CMS (just HTML and CSS). You could also get by with using an existing HTML page that already does these things. Lets say you've now got that in a file called home.html. With a fresh install of ProcessWire (using the included profile, not the blog one), you'd copy your home.html file to /site/templates/home.php. Now view the homepage on your site. You should see your page. If some things are broken, then that is because the links to the CSS files and other resources have changed. Typically we place our CSS files in /site/templates/styles/ and our javascript files in /site/templates/scripts/. But you can place them wherever you want. Wherever you place them, you'll want to update your /site/templates/home.php file to reference them directly. So if you had a line at the top that said this: <link rel='stylesheet' type='text/css' href='css/style.css' /> Then you'd copy everything from that old 'css' directory into /site/templates/styles/, and then update your code to say this: <link rel='stylesheet' type='text/css' href='/site/templates/styles/style.css' /> Better yet, make it say this, so that it will continue working no matter where you happen to move your site: <link rel='stylesheet' type='text/css' href='<?=$config->urls->templates?>styles/style.css?>' /> With that line above, we're asking ProcessWire's $config variable for the URL to the templates directory. Since it determines that at runtime, it'll work whether your site is running from root or a subdirectory. Once you've got your homepage looking the same as it did outside of ProcessWire, then you are good to move forward with the next step, which is to make it dynamic. This short tutorial will get you started and give you what you need to know to proceed with your magazine style blog homepage. There are also more tutorials in the Wiki that you may want to check out after covering the basics.- 3 replies
-
- 3
-
-
- blogprofile
- magazine
-
(and 2 more)
Tagged with:
-
You should be able to do this during runtime: $page->template->filename = '/path/to/file.php'; or $t = $templates->get('some-template'); $t->filename = '/path/to/file.php'; Note that /path/to/file.php must be a full path and filename relative to the server's file system, but PW won't accept it unless the file ultimately lives somewhere under your web root where PW is installed. Meaning, you can't do this: $t->filename = '/etc/passwd';
-
Hi John, This is a basic contact form that I've used in the past. I might help. Cheers Marty <?php /** * basic-form.php - Example of a simple contact form in ProcessWire * */ // set this to the email address you want to send to (or pull from a PW field) $emailTo = 'email@site.com'; // or if not set, we'll just email the default superuser if(empty($emailTo)) $emailTo = $users->get($config->superUserPageID)->email; // set and sanitize our form field values $form = array( 'fullname' => $sanitizer->text($input->post->fullname), 'email' => $sanitizer->email($input->post->email), 'comments' => $sanitizer->textarea($input->post->comments), ); // initialize runtime vars $sent = false; $error = ''; // check if the form was submitted if($input->post->submit) { // determine if any fields were ommitted or didn't validate foreach($form as $key => $value) { if(empty($value)) $error = "<p class='form-error'>Please completed all fields.</p>"; } // if no errors, email the form results if(!$error) { $subject = "Message from your website contact form"; $message = ''; foreach($form as $key => $value) $message .= "$key: $value\n"; mail($emailTo, $subject, $message, "From: $form[email]"); $sent = true; } } if($sent) { echo "<p>Thank you, your message has been sent.</p>"; // or pull from a PW field } else { // encode values for placement in markup foreach($form as $key => $value) { $form[$key] = htmlentities($value, ENT_QUOTES, "UTF-8"); } // output the form echo <<< _OUT $error <form action="./" method="post"> <label for="fullname">Your name*</label> <input type="text" id="fullname" name="fullname" size="60" value="$form[fullname]" /> <label for="email">Your email*</label> <input type="email" name="email" id="email" size="60" value="$form[email]" /> <label for="comments">Message*</label> <textarea id="comments" name="comments" rows="5" cols="43">$form[comments]</textarea> <input type="submit" name="submit" value="Submit" /> <p class="form-required">* required fields</p> </form> _OUT; } ?>
-
Edit well if there not too many projects you could try and evaluate the dates and store them on runtime to sort by that value afterwards when you loop them out. $pa = $pages->find("template=project"); if(count($pa)){ foreach($pa as $p){ if(!$p->numChildren) continue; // no assignments // store a temporary value to the project page $p->tmp_date = $p->children("sort=-due_date")->first()->due_date; } // output the projects foreach($pa->sort("-tmp_date") as $p){ echo "<li>$p->title</li>"; if(!$p->numChildren) continue; foreach($p->children("sort=-due_date") as $a){ // output assignments } } } This will work well if you don't have thousands of pages. The module approach would be the more efficient and scaleable.
-
FieldtypeMapMarker doesn't implement a getMatchQuery method, which is required in order to support any operators that don't exist in MySQL. For instance, MySQL knows what "=" and ">" are, but it doesn't know what "%=" is. So I'm guessing you are getting a "Operator '%=' is not implemented in FieldtypeMapMarker" error message. The second issue is that there isn't actually a field named 'address' in the DB, as this fieldtype represents it with the default 'data' field. Meaning you would just refer to it as "map_marker" (or whatever you field name is) rather than "map_marker.address". But the truth is, since we're using 'address' as in this Fieldtype at runtime, we really should allow it for queries too. Try adding this to your FieldtypeMapMarker.module, which should solve both issues: public function getMatchQuery($query, $table, $subfield, $operator, $value) { if(!$subfield || $subfield == 'address') $subfield = 'data'; if($operator != '%=') return parent::getMatchQuery($query, $table, $subfield, $operator, $value); $ft = new DatabaseQuerySelectFulltext($query); $ft->match($table, $subfield, $operator, $value); return $query; } Let me know if that works out for you, and I'll add it to mine too. I'm also going to add a fulltext index to the 'data' field so that we can use other partial match operators.
-
I myself am a static man - I love Middleman, Jekyll, Stacey & Kirby. I'd really love it if there was some crossover, something where you'd write your website, build it and then upload it having some tiny PHP router for doing tiny bits of runtime changes, like including different files based on $ajax, sending forms, etc.
-
Translation doesn't work. Or am I missing something? ProcessLanguageTranslator: Found /site/modules/ToolsTimeAgo.module ProcessLanguageTranslator: That file has no translatable phrases Translation doesn't work for runtime variables as the string. And I thought the _() have to be on 1 line, but I could be wrong.
-
Page draft module - useful to anyone? Please feed back!
ryan replied to Rob's topic in Module/Plugin Development
If you don't specify a permission, then your module is available to all admin users. If you specify an existing permission, like 'page-delete', then your module's execute() method(s) will only be available to users that have that permission. You can also have your module install a new permission, and then set it as the required permission. Example: $p = wire('permissions')->add('page-draft-control'); $p->title = 'Ability to create draft pges for edit/preview'; $p->save(); I think that the above is probably not exactly what you are looking for. In your case, you are using a Process module as an 'autoload' module, so your hooks are getting attached regardless of any specified permission (since that permission only applies to 'execute' methods). What you should do is check that the actual $page is editable() before performing runtime actions: public function hookPageListActions(HookEvent $event) { $page = $event->arguments[0]; if(!$page->editable()) return; ... } public function executeCreate() { $page = $this->pages->get((int) $this->input->get->id); if(!$page->id) throw new WireException("Page doesn't exist"); if(!$page->editable()) throw new WirePermissionException("You don't have access to edit that page"); ... } -
You can't use any operator except '=' when querying path or url from the database (like when using $pages->find). This is because the path doesn't actually exist in the database, so there's nothing to perform comparisons on. It is generated at runtime based on a page combining its name with the names of its parents. It worked with '=' because we pulled a few tricks to convert a path into a big left join statement, in an attempt to match it. And this actually works very well. But it doesn't translate quite as well to other operators. I should have had the engine throw an exception when you used an operator it didn't support for that. Instead it just switched it to an '='. So I've updated it so that it now throws an exception instead. I've also added a new module to the core, called PagePaths. When you grab the latest dev branch, do a 'check for new modules' and you should see it. Once you install that, it goes and sets up a table with all the page paths and a means of querying them. This enables you to use any operator when querying path or url, including all the partial text matching ones like %=, *=, ~=, ^=, $=. As a side effect, this module also brings potential performance improvements to other queries, as it eliminates the need for the left join trick I mentioned above. (Though in my initial tests, it doesn't seem to be a measurable improvement). I will probably have this module installed by default for new installations of 2.3. But won't have it auto-install to existing installations. That's because it has to generate an index of all pages in your site--a potentially resource consuming process. For instance, Antti's 100k page site probably won't work with this, as it's no small task to go and build an index for 100k pages after the fact. But if you aren't running a massive site already, this module is one you probably want on most sites, so I went ahead and included it in the core.
-
Diogo, you don't have to do it manually, just a save hook. Giving you back a simple find() on front-end it is worth considering it. It would also even work in admin sorting by the field and so on. Sure an simple page array where you store the height of the first image in a runtime property to then sort by it is also a way. But then just add a line or two more to save the result to the page and you already have a "function" to do it automaticly. $pa = new PageArray(); foreach($pages->find("template=xyz") as $p){ $h = $p->images->first()->height; $p->img_height = $h; $pa->add($p); } foreach($pa->sort("-img_height") as $p){ $img = $p->images->first(); .... }
-
You won't be able to do this at the DB level because width/height is not stored in the DB. As a result, you won't be able to query the DB based on height of an image unless you manually store it yourself in some other field. However, I think you could probably do a $page->images->sort("height"); at runtime.