Jump to content

BitPoet

Members
  • Posts

    1,279
  • Joined

  • Last visited

  • Days Won

    57

Everything posted by BitPoet

  1. This is a classic case for OR-groups. parent=/portfolio/, sort=sort, (project_location_london=camden), (project_location_elsewhere=midlands)
  2. If you want to sort the artists differently for each genre, you'll probably have to reverse the logic and add an "artists" page field to the genre template and put the artists there. Each page then keeps its own sort order for its field contents, so your client can sort the artists however he wants. It doesn't scale as well though if you have many artists.
  3. You could perhaps use OR-groups in the selector (untested): $options = array( "level" => true, "max_levels" => 2, "selector_level2" => "(parent!=$lastpageid), (id=$items)" );
  4. You're mixing two concepts here: First, the native parent-child relationship that PW provides. ABBA is a child of the Pop page (category). This is what the backend shows and every page can only have one parent. Second, you have relationships through page fields. This is completely structure-agnostic. You're actually making things a little complicated by placing the artists underneath a category. Easier would be to have a page "artists" underneath "music" with its own template and under which you have all artists, no matter what category they belong to, and simply set all the categories for the artists like you already do. Presentation of the relationship for the frontend happens in your PHP template file for music-genres, where you get all the artists for the category with a selector like $artists = $pages->get("template=music-artist, music_genre_types={$page->id}"); instead of calling $page->children. You could mix both concepts and merge $page->children with the result of the selector above, then your current approach would still work.
  5. You may be running into a key length issue if your asymetric key has less bits than the data (plus a small amount of padding that openssl adds to close some attack vectors) you try to encode. In your example you have 172 bytes of data, which is too long for a 1024 bit RSA key. You can explicitly state the desired key length through the private_key_bits configarg when you generate a key pair with openssl_pkey_new.
  6. @Nukro: I'm pretty sure that you need to use the openssl_pkey_get_[private|public] functions to read back a key from a PEM file to get the encrypt/decrypt functions to work.
  7. Before you duplicate "home" you have to change the "Can this template be used for new pages?" option in the home template's family settings, as it's set to "no" by default.
  8. You need to specify the name of the file field used in the repeater (each repeater item is technically a page). Thus, iterating over $page->document_downloads returns pages and you need to explcitly state which field of that page you want to access, just like with a regular page. foreach($page->document_download as $download) { $file = $download->name_of_your_file_field; $fileurl = $file->url; }
  9. I used SSI way back when hosting packages with PHP were too much for small sites but you could get small packages for a Euro a month that supported SSI and Perl CGIs. I haven't found a use for them in years though, as they're really limited and, if you're already working in a PHP environment, tend to break apart code and template logic. So I don't have much to add here, but I'm curious to hear about practical use cases.
  10. There's no reason why this shouldn't work, and there should be no complicated tricks involved in making it work. There's something else going wrong, probably in a part of a script not shown here. Letting JS pass on information that should already be implicitly available to PHP means hiding the problem, not solving it. Usually, AJAX requests should implictly pass on the cookie if the requested page is on the same server as the page requesting it. If you can verify that the wire cookies are getting sent along, you'll have to look for something else. Did you try stripping down your getitems script to the bare minimum, then clearing the cache to make sure you don't see stale data, and introspect the data sent and received in the browser's debugger?
  11. For the input type, you should be able to set it like this: $f->set("inputfieldClass", "InputfieldCKEditor"); The text formatters setting works the same, only it expects an array of values (in order of execution): $f->set("textformatters", array("TextformatterEntities")); There's also documention for inputfield dependencies in the API docs.
  12. Are you sure your template is basic-page (not just another template that includes it, like home)?
  13. A single master key page (or storage location) is definitely involved, but it's not enough (you need to secure that key too, or you'll deliver the encrypted information in the database together with the key to decrypt it to the hypothetical attacker). You could still use my approach from above, just instead of creating a fresh key from scratch, use what I suggested as a password recovery mechanism to populate the users' profiles with the master key encrypted using their password hashes (basically, leave out step 2a). Everything else like creating a temporary session key and storing the key for field decryption and encryption - just in this scenario the master key - session-encrypted stays. So the steps would be: Once: First step is again to create an asymmetric public/private key pair for admin, save the private key off-site (best print it out too and store it in the safe) and puts the public key into config.php (you could e.g. use openssl to generate that key pair) Once: Admin creates a symmetric key (master key), encrypts it using the public key and stores it Admin creates users and assigns their passwords (or changes the passwords for existing users) In the same step, admin caclulates the user's password hash, decrypts the master key in config.php using his private key, then encrypts the master key with the user's password hash and stores it in a field in the user's profile Now, steps 1 to 6 with the exclusion of step 2a from above once again work, only instead of separate keys, every user decrypts and encrypts their copy of the master key. This should be a simple as it gets while you still avoid storing an unencrypted key at any point. There would be the theoretical possibility to use the admin's password hash to symmetrically encrypt and decrypt the master key in config, but that would mean there could be only one admin user (it would also introduce a predetermined breaking point when admin changes their password and forgets to update config.php with the re-encrypted key, which would result in garbage in the key copies assigned to users from this point on and, in the worst case, service information encrypted using such an invalid key). This system still has one known weakness: if an attacker acquires a copy of the database, they only need to crack one password to decrypt all service credentials. If you want to avoid that, things get exponentially more complicated because you would need to create a unique key for every service, store an admin-encrypted version of it somewhere and store (as well as update) a table of key <-> service mappings for every user. In turn, as reseting the user password every time access to a service is granted isn't practicable, you'd need a mechanism to encrypt the key for the user without knowing the user's password, leaving you with asymmetric encryption for that part. The user can't be asked to carry around and type in their private key though, so you need to store that private key for them encrypted using the user's password hash. It's another layer of indirection, and, as mentioned in my first reply, you'd need some kind of automated key infrastructure to make this easy to handle in code, and you'd also have to update two key fields and the whole mapping table with every password change (I'm not even going into the possible mayhem if password change by the user and service grant by the admin manage to overlap).
  14. @Nukro: What you have so far looks good to me. Your question with the user registration is a valid one, and re-reading my post I found that I left out a step (I was working on our intranet site at the moment where I always have the user password available due to http basic auth, but that's not a given). Thankfully, the scenario gets a little less complicated if each user only needs to see their own passwords. Basically, yes, you need to populate your user's key in the registration (or password change) step. The flow should look like this: 1) User logs in and PW checks if the key in the user template is already populated 2a) If not, create one, encrypt it with the hash of the user's password and store it 2b) If yes, decrypt it using the password hash Now you have the unencrypted key in memory. But you probably don't want the user to have to enter their password every time they view a stored login (or you'd be as good as done here). So, to be able to use the key while the user stays logged in, you need to store it in a way that it can be decrypted without giving the password. The best option for that is session storage. Thus the next steps are: 3) Create a random key (session key) and store it in a session cookie (lifetime 0) 4) Encrypt the user's key using that session key and store it in the user's session 5) Now, with every consecutive visit while logged in, you can read the encrypted real key from the session and decrypt it using the session key in the cookie Even though the encrypted key is stored in the session, it's at no time saved in plain text. 6) Make sure that if a user changes their password, they need to give both their old and new password and re-encryped and store the key again. You have only one question left now: do you need to add protection against password loss? If yes, it gets a little more involved (though not awfully so). You can add a recovery mechanism like this: - Create a public/private key pair for the "recovery agent" that is long enough to asymetrically encrypt the user's key. Keep the private key out of the site (store it in a safe place where you find it again) and make the public key accessible to your code - In steps 2a and 6, also encrypt the user's key with the recovery agent's public key (let's call the result the backup key) and store it somewhere (e.g. another hidden field in the user template) - Create a password reset form where the admin/recovery agent can re-set the user's passwords and enter the private key. Now, when a user loses their password, the following steps happen: - When the admin/recovery agent assigns the new password, decrypt the backup key using the private key, re-encrypt it with the user's password hash and store it just like in steps 2a and 6 - You best make sure that the user changes their password again. Now the regular mechanism of step 6 works again
  15. BitPoet

    CloudCannon

    I just gave the documentation for CloudCannon a read, and I can't agree with you. You still have to think about how you structure it, decide what information to turn into individual fields (editable regions) and how you name them for translation (manually give every editable region a unique data-i18n key) and styling. The example for the navigation with an endless list of if-statements just to decide which link should have the "active" css class set is enough in itself to make me never ever want to use it as a CMS. The "problems with PW" you mentioned are things every developer should always (at least try to) work out in advance before he/she starts working on a site, no matter what platform. Even a static site needs fixed places for pieces of identical semantic value and proper naming of HTML elements for the CSS and JS that is referencing it to work and still make sense after a year. If you encounter things that you always need to do the same way to get started with a website in PW, you can create a site profile, use one of the Exporter modules or write a short script. This is one of the big strengths of PW: to make complex recurring tasks easy.
  16. The exclamation marks are simply pattern delimiters. Normally, this is "/", but PHP looks for the first character in a pattern string and treats it as the delimiter. This comes in handy whenever you have a pattern containing a forward slash, as you'd otherwise have to escape it to tell PHP that it's part of the pattern and not its end.
  17. @justb3a: my personal suspect is different warning levels between the php.inis of the working and non-working system, as the error sounds like HTML injected into a JSON message.
  18. I can't see any reason (though I don't rule out that I'm missing the obvious) why running the same selector for each word inside a single query shouldn't work as expected. $sel = array(); $parts = explode(' ', $parts); $selfields = "title|body|collection.title|hue.title|pattern.title|pattern_type.title|usage.title"; foreach($parts as $part) { $sel[] = $selfields . "*=" . $sanitizer->selectorValue($part); } $found = $pages->find(implode(', ', $sel));
  19. AFAIK all that's needed should be what ProcessPageEdit::___buildForm does for the "Save + Keep Unpublished" button: $submit2->class .= ' ui-priority-secondary head_button_clone';
  20. @Nukro: A few points that may help you get into the right direction: Never use ECB mode. With the same key, it always produces the same output for the same input, thus making patterns obvious and rainbow table attacks cheap. If you see an example on a web page that uses ECB, navigate somewhere else (that includes the examples at php.net). Never store keys in clear text or with weak encryption on the same system where you hold the data (or have it easily accessible), or you're replacing security by obscurity. Whenever you use symetric encryption, use an initialization vector (and make sure your cipher mode, e.g. CBC, supports IVs). It is much like the salt in password hashes and needs to be random for each encrypted entry and reasonably unique. Prepend the IV before you store the encrypted data and extract it for decryption. Depending on how you model your system, implementing a secure system can get slightly tricky. If visibility of information shouldn't be limited to a single PW user, they all need to know the key (let's call it the 'master key') used. To avoid storing it in plain text, encrypt and store the master key for every user. Use a secure, repeatable hashing function to generate a long enough encryption key from the user's password for this step. On password change, re-encrypt and store the key (and, again, don't forget to use IVs). Of course, in such a system, all user passwords need to be strong, or an attacker with access to the DB contents could easily crack the encryption to the master key through brute force. To populate the encrypted master key for new users (or if you add the feature to a system with existing users) you have to assign their password from an account that already has the correct master key. This way, at least no plain encryption key is ever stored in the system. There are more elaborate (and, of course, even more secure) approaches, but these would require an automated pulic/private key infrastructure and off-site services. That's what the "good guys" among the big internet companies use, but things get really complex at that point.
  21. There's this part in AdminThemeDefaultHelpers.php that I'd call a likely suspect for the missing template file issue. public function renderTopNavItem(Page $p, $level = 0) { $isSuperuser = $this->wire('user')->isSuperuser(); $showItem = $isSuperuser; $children = $p->numChildren && !$level ? $p->children("check_access=0") : array(); $numChildren = count($children); $out = ''; if(!$showItem) { $checkPages = $numChildren ? $children : array($p); foreach($checkPages as $child) { if($child->viewable()) { $showItem = true; break; } } } if(!$showItem) return ''; No clue if this is by design or if viewable is simply the wrong check at this point. I guess the correct thing would be to check the current page's permission (through the module info's permission field if present like it does further down when genererating child page entries), and the loop above was meant to hide items without children from the top navigation (I'm not deep enough into these parts of PW to judge if that is needed).
  22. Perhaps just a missing $destinationPage->save(); after the foreach?
  23. It says the warnings were sent six days ago, so it looks like they got stuck somehow. Clearing all sessions might be worth a try.
  24. <div class="pagination"> <?php $portfolio_pages = $pages->get('/portfolio/')->projects; $prev = $page->prev($portfolio_pages)->id ? $page->prev($portfolio_pages) : $portfolio_pages->last(); $next = $page->next($portfolio_pages)->id ? $page->next($portfolio_pages) : $portfolio_pages->first(); ?> <div class="left-arrow"> <a href="<?php echo $prev->url; ?>" title="Previous Project"><?php echo file_get_contents('assets/static-images/left-arrow.svg'); ?></a> </div> <div class="all-projects"> <a href="<?php echo $config->urls->root; ?>portfolio/" title="All Projects"><?php echo file_get_contents('assets/static-images/all-projects-icon.svg'); ?></a> </div> <div class="right-arrow"> <a href="<?php echo $next->url; ?>" title="Next Project"><?php echo file_get_contents('assets/static-images/right-arrow.svg'); ?></a> </div> </div> $portfolio_pages is a PageArray. $page->prev/next looks for the position of $page in the PageArray, then returns the preceding/following page. In case of the first/last item, where there is no such, it returns a NullPage object with an id of zero. So you can check for that id and get the last/first item in the PageArray instead.
×
×
  • Create New...