Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/02/2014 in all areas

  1. Dynamic Roles are a powerful access control tool for ProcessWire. They pick up where traditional roles leave off, and allow you to assign permissions at runtime based on any factor present with the user. Once a user receives one or more dynamic roles (at runtime), those dynamic roles then specify what pages the user can view, edit, or add children to. If traditional roles are a sledgehammer, Dynamic Roles are a scalpel, allowing nearly any finely tuned access control scenario. Traditional ProcessWire roles are limited to assignment of view/edit/add access on a per-template basis. Dynamic roles go outside those limitations and enable you to assign that access based on any factors present with a page (i.e. match any field values). Dynamic Roles assign new access, but do not revoke existing access provided by traditional roles. As a result, Dynamic Roles can be used together with traditional roles, and the two work beautifully well together. Though Dynamic Roles can also replace all situations where you would use traditional roles for access control assignments. If using Dynamic Roles to assign page-view access, you would typically want to use traditional roles to revoke view access from at least the "guest" role at the template level. Then use Dynamic Roles to assign view access to those pages in a more granular manner. This module directly affects the results of all page getting/finding operations by applying the access control directly to the database queries before pages are loaded. As a result, it is fast (regardless of scale), pagination friendly, and requires no further intervention by the developer other than configuring the dynamic roles as they see fit. Because it relies upon new features present only in ProcessWire 2.4.6+, it requires the current dev branch. Sponsored by Avoine Concept by Antti Peisa Code by Ryan Cramer PLEASE NOTE: This module is in pre-release state (like the PW dev branch it requires) and is not recommended for production use just yet. Though we do appreciate any testing and/or feedback that you are able to provide. While not required, this module benefits from ProFields Multiplier. If you have ProFields Multiplier installed before installing this module, it will make this module more powerful by making all of your access control selectors have the ability to use OR-group conditions. Depending on your access control needs, this enables you to accomplish more with fewer Dynamic Roles. How to install Make sure you are running ProcessWire 2.4.6 (dev branch) or newer. Download from GitHub (we will add this module to the Modules directory later). Place all files from this module in /site/modules/DynamicRoles/. In your admin, go to Modules > Check for new modules. Click "install" for the Dynamic Roles module (ProcessDynamicRoles). Click to Access > Dynamic Roles for the rest (see example and instructions below). Example and instructions Lets say you ran a Skyscrapers site and wanted a role enabling users with "portmanusa.com" in their email address to have edit access to skyscrapers designed by architect John Portman, with at least 40 floors, and built on-or-after 1970. Yes, this is an incredibly contrived example, but it is an example that also demonstrates the access control potential of this module. 1. In your admin, you would click to Access > Dynamic Roles. 2. Click "Add Dynamic Role". Enter a name for the dynamic role, like: "skyscraper-test-editor" and save. 3. Under "Who is in this dynamic role?" section, click "Add Field" and choose: Email => Contains Text => "portmanusa.com". This will match all users having "portmanusa.com" in their email address. 4. Under "permissions" check the boxes for: page-view and page-edit. 5. For this contrived example, we will assume the user already has view access to all skyscrapers, so we will leave the "What can they view?" section alone. 6. For the "What can they edit?" section: Click "Add Field" and choose: template => Equals => Skyscraper. Click "Add Field" and choose: architect => Equals => John Portman. Click "Add Field" and choose: floors => Greater Than Or Equal => 40. Click "Add Field" and choose: year => Greater Than Or Equal => 1970. 7. Click Save. Now users matching the conditions of your dynamic role will be able to edit the matching pages, but not any others (unless assigned by traditional roles).
    18 points
  2. Granted, there's a lot of truth in that statement. However, there is another perspective that small businesses can value in a CMS – the way it can make maintaining their web site easier, saving them time, effort and, well, nerves. Everyone who has worked with PW will agree that it makes it very easy to customize the backend to a specific user's needs and ability. Now, that is something most people understand very well since they have usually at some point in the past tried to build and maintain the web site themselves using, let's say, not-so-great tools like your typical “1-click web site construction kit” provided by their (cheap) web hoster. Those clients usually can be pretty impressed by how a customized backend can be way less frustrating for them and help them actally fulfill their day-to-day tasks (let's face it, for most small business clients it's more like month-to-month tasks). I have never considered myself to be an artist or something, in fact I even have my difficulties with “designer” (especially since I don't have a formal education, much less a degree in that field). I consider myself to be a craftsman. However, with small business client's, I often find that it's necessary to also act as a consultant or even advisor. As you stated, most of these clients don't know much about our craft or the tools we use – moreover, a lot of them don't even know much about the internet and marketing in it. If they do, their knowledge is outdated. I guess what I'm trying to say is what can also help is to communicate to small business clients is that what you (in addition to your craft) provide is help. (In fact, one of my clients literally refers to me as his “web site helper”.) They won't get much help and advice from any cheap chop shop selling them a generic rip-off design, and it's something some of them might be able to relate to.
    4 points
  3. Hi bruto, this is all very sad and discouraging to hear. Are you in the south of Europe? We all know that economy is very low there and there is very little you can do as an individual to improve the situation. However, I'm a believer in marketing strategy and if my rates would go as low as yours I would first ask myself whether I want to stay in business or not (as you did of course). Second I would ask myself about my unique selling proposition: Is there something unique in my work that sets me apart from the competition? Do I communicate my USP clear enough (read: do people understand what sets me apart)? And - last but not least - is my USP relevant to my customers? Finding your USP is not an easy thing but - if it matches a customer demand - the only thing that can move your business forward and raise your rates. In my personal opinion the best way to create uniqueness in the field of web development is via a unique design language. I've never come across a customer who was able to value - or even understand - the technique behind his website. Processwire? Wordpress? Easy to maintain? Future orientated, secure, lean and fast? They simply don't care. But if it looks "nice" and outstanding, they feel emotional involved and "touched". Hope that helps a bit.
    4 points
  4. I'm getting this right now (right after updating from 2.4.3 to 2.4.5), so it's definitely an outstanding issue. Will have to dig in to see what's going on.. Edit: Just guessing, but could this be related to this commit and the part about "6) Convert Fieldtype modules to load on-demand rather than all at boot"? Looks like after the update when InputfieldCropImage runs, FieldtypeCropImage hasn't been init'd yet.. which obviously results in getThumb() not being available. Anyway, will have to debug more. Edit 2: Whether or not that was the issue, I've solved it locally by making sure that FieldtypeCropImage is loaded with InputfieldCropImage. I can't uninstall the module or try other tricks on this site, as it's already in production -- I'm assuming that Antti or Ryan will dig out the real reason (and fix) for this, but in the meantime this works for me: Index: InputfieldCropImage.module =================================================================== --- InputfieldCropImage.module (revision 7702) +++ InputfieldCropImage.module (working copy) @@ -13,7 +13,8 @@ public function init() { parent::init(); - + // make sure FieldtypeCropImage is loaded + $this->modules->get("FieldtypeCropImage"); //default settings $this->set('thumbSetting', 'thumbnail,100,100'); }
    4 points
  5. There seems to me to be a growing misunderstanding of what a website actually is. Leaving aside ecommerce, which is a very specific and obvious function, I think many companies look at a website as basically another advert. My background is in above the line media, especially TV and Radio, and I have spent a huge number of years creating adverts that bring a customer to a clients door. Once they are there, it is the client's responsibility to make sure the customer buys something, gets some sort of satisfaction or service and wants to come back. It is a very simple, linear process. When website designs first started, there intent was a little more blurry - they were not quite an advert, nor a brochure, could contain a huge amount of information and could be as stuffed full of words as you wanted. A lot of companies seem to work on the principle that if people found your website, you should give them lots of value in the form of information so they would then want to hit the contact button and consolidate some sort of relationship. Then along came the SEO position developers. They threw out the idea of value relationships and instead turned websites into instant ads. The entire idea was to drag people to the website at any cost and with any luck, get them to contact you. The perfect SEO website is formulaic, basic, fast, uncluttered to the point of utilitarian and offers no customer value at all - that is not what it is there for. It is back to my radio and TV ads where all we tried to do is get the customer to the clients place of business; to their door. The problem is that a website is not the clients place of business. There is a very good chance with an SME that it is impossible to monitor it, have live chat or anything else that a customer would get if they actually turned up at the place of business. The site is just another ad - so all that has happened is that you spend possibly a lot of money on SEO and online advertising just to persuade people to go to a web address to simply see another advert .... probably a not very well designed one at that. But that is where we are at. Companies that make their lively hood online are spending money on their websites because those really ARE their businesses. But companies that are using the web simply to tell people about themselves are being persuaded by cheap, SEO orientated marketing companies to produce low value, template based, formulaic adverts that address no brand considerations what so ever. Indeed, my very little experience with these companies trying to sell ME a website for my music company has shown that they never ask a single question relating to my brand, brand values, product targeting or the million and one other facts that are kind of basic for any serious advertising campaign. I can write copy that will make a company dance and sing - I really am very good at it. I can make cute things dimple like a Disney princess, make serious services like lawyers sound like sage advice from your favourite grandpa, or make your garage service sound like the best thing since sliced Model T Ford. But that does not fit with the formula. Copy and design like that needs research, time spent with the client and that thing that scares people most of all - imagination. And none of that is cheap. The hard part is persuading the client that the monthly standing order he is paying for the seo driven, same-as-everyone-elses template site might be getting him a handful of orders, but in the long term could seriously undermine his brand. I have no idea how to do that as I find more and more that when I deal with people and talk about the importance of brand, of good customer relations and of building something of value, that they look at me as if I am talking a foreign language. I am beginning to feel a little old!
    4 points
  6. I do a lot of form based websites/applications. Since I started using ProcessWire, I haven't had a need to deal with unreliable Captcha schemes and haven't had to deal with spam. As Ryan stated, using honeypots is a preferable option.
    2 points
  7. As it's presented in the footer of the page, this forum doesn't run in ProcessWire, but on ipboard. I can remember reading a post of Ryan mentioning, that one should use a forum software for forums, which I would also suggest. You could always integrate the forum software in ProcressWire to share things like user logins and accounts.
    2 points
  8. <?php // home template $session->redirect($pages->get('/')->children->first->url); // or even easier because it is already the "homepage" $session->redirect($page->children->first->url); ?>
    2 points
  9. There is another issue here, one that applies to CMSs in particular. I think it is really important for small companies (5 people or less, perhaps) to be able to update their website themselves - for many small companies, a monthly fee is not realistic for the amount of value they are going to get from the site. However, I have been stunned by the number of company owners who simply cannot put together two words in any meaningful way, or even in a grammatical way. We seem to be in a situation that being able to use Facebook or some communication app is seen as more important than actually being able to write; it is as if the software will somehow make you a great communicator without you having to try. I just don't get this. Thanks to the way the internet works by default (text on screen), people read more than ever, even if it is just a chat message. And yet, their communication skills, the ability to use words clearly and nicely, seems to be getting worse and worse. And then businesses wonder why they don't get work? All a bit sad, really.
    2 points
  10. €1 000 for an ecommerce site is simply exploitation. I did some research a while ago on average cost of websites - can't find the same results right now (there was a nice survey), but found some others. From executionists.com: http://www.howmuchdoesawebsitecost.com/averageweb-design-pricing/ The "Web Design Calculator" of webpagefx.com gave $3 000 as the minimum price for a basic ecommerce site with nothing else, even styling. With moderate styling, standard CMS features and advanced level ecommerce the price range went to $10-21k.
    2 points
  11. <?php function dmc_list(){ $dmcs = wire('pages')->find("template=dmc"); $dmcs->sort("parent.title"); $total = $dmcs->count(); $items_per_column = ceil($total / 3); $count = 0; echo"<div class='row'>"; echo "<div class='col-md-4'>"; //start the first column echo '<ul>'; // start your first list foreach($dmcs as $dmc){ $new_region = $dmc->parent->title; // why the same variable? $region = $dmc->parent->title; if($region !== $new_region) echo "<li class='list-group-item list-group-item-info'><b>{$region}</b></li>"; echo "<li>{$dmc->title} {$region}</li>"; if(++$count%$items_per_column) { // this will be false when we are on the last item in column echo ""; } else { // this part will run when we are on the last item in the column echo "</ul></div>"; if($count === $total) echo "<div class='col-md-4'><ul>"; // this will start a new column unless we're on the last item. } } // end foreach echo "</div>"; // end row using the modulus : $a % $b Modulus Remainder of $a divided by $b. so if the items_per_column is say 40, then there will be no remainder when the count hits 40 (false condition) so then it uses the false clause and closes the tags, and start the new tags; not perfect, but should give you some ideas about how to use some tricks to make it cleaner
    2 points
  12. This module integrates Swift Mailer mailing library to ProcessWire, providing support for three different "transports" or methods of sending email: SMTP, Sendmail and Mail (essentially PHP's native mail() function). WireMail is email-related base class for ProcessWire. See this post by Ryan for the details. Important thing to note here is that a) it's brand new, so as of this writing you'll need a fresh dev version of ProcessWire to use this and b) it makes integrating new ways of handling email-related tasks very easy. Getting started You can download or clone the module from GitHub: https://github.com/teppokoivula/WireMailSwiftMailer/. Using this module is as simple as downloading / cloning it to your modules directory and installing it. If you're going to use SMTP or Sendmail features, insert correct SMTP credentials / Sendmail command to module settings first. Third transport, Mail, is included simply because it's a native feature of Swift Mailer; if you're going to use it, I would suggest against installing this module. ProcessWire's native WireMail implementation handles this part just fine. Basic usage Sending emails should be done using wireMail() function -- if you use mail() directly, you're going to bypass ProcessWire's email handling features entirely. Main difference between mail() and wireMail() is the order and number of arguments: $number_of_recipients = wireMail($to, $from, $subject, $body); For more information please take a look at README. This module is released under GPLv2 (just like ProcessWire itself) with the exception of included Swift Mailer library, which is copyright © Fabien Potencier and released under the MIT license.
    1 point
  13. A simple module that'll automatically obfuscate all emails found on the page. Emailobfuscator.zip
    1 point
  14. This Textformatter module for ProcessWire enables you to break up a single textarea field (using either TinyMCE or CKEditor) into multiple pages. You include all of the content in a single rich text field and separate each pagination with a line of hyphens (5+). When rendered on the front-end, the user will see pagination links at the bottom of the page enabling them to move forward and backward through the content/article. Also included is the option for title pagination. That means assigning a title/headline to each pagination and giving the user a list of those titles they can click on to move to each section of the article. Example of this module in action Documentation and customization options GitHub Page Module Page Download ZIP Install class: TextformatterPagination
    1 point
  15. couldn't help my self from posting this, please forgive http://blog.sucuri.net/2014/07/remote-file-upload-vulnerability-on-mailpoet-wysija-newsletters.html http://www.binarymoon.co.uk/2014/06/new-timthumb-exploit-found/
    1 point
  16. Hey fellas, First time poster, long time lurker here. I've worked as a freelance web developer for the past 6 years, and, although things have not always been easy, I've been able to sustain a steady and decent income until recently. I specialize in CMS-powered and e-commerce websites and, in general, most of my clients are SMBs. So, what's the problem? Well, it's simple, lately I'm finding it incredibly hard to get businesses to accept my proposals. Granted, it's not an overnight thing, it's not that last year they were all happy with my fees and now they aren't, but I suppose over the last two years or so, there's been a downward trend as far as fees go. Now, I have an idea why this is happening. In the country where I operate, the average fee for a simple, static website is in the region of €300-350, and for a CMS website not that much more really. E-commerce websites normally command a higher price tag, but we're talking sub €1,000. My fees are significantly higher than these, and, honestly, no matter what I tell prospective clients, they just plain refuse to listen to my arguments. They simply don't care. They don't care whether their websites are all based on poorly coded templates downloaded from the Internet, they don't care if hundreds of other websites look exactly like theirs, most of them don't care if their website loads fast or slow as hell. In fact, they don't even care about security, as long as it's "secure enough". I suppose all of this is to say that I'm no longer competitive in this crap-as-hell-and-cheap market. My high quality, specialized services are no longer valued, or even needed. This has put me in a very awkward position, to the point where I need to radically change my strategy or simply close up shop. Honestly, the only alternative I see is to basically follow suit, stop creating handcoded, unique websites and just sell template-based ones for the average going fee, significantly lowering my standards along the way. I could do that, but every single cell in my body shivers at the mere thought of it. It's just sad, I got into this business because I like to solve problems, and I've always thought that people pay me precisely for that, to solve their problems. So, here I am now, asking for your opinion and advice. What would you guys do? Have you found yourself in a similar situation? Should I close shop? Should I become a template monkey? Thank you all.
    1 point
  17. roles.on speed u sholud.call it adderole
    1 point
  18. @Adrian, Does what it says on the tin. No errors and the id of the assets/files folder where the zip is stored matches id of Migrator page..
    1 point
  19. Might be simple, but just because you can doesn't mean you should
    1 point
  20. It's been debated a few times before. https://processwire.com/talk/topic/6468-symphony-forum-and-pw/ https://processwire.com/talk/topic/3536-forum-integration-module/ https://processwire.com/talk/topic/572-release-discussions/ I'm currently working on a simple forum based on comments, but it's very basic and isn't a module, so it's not something I'll be releasing etc, it just fits in with my site. If you want a forum like this one, then you'll definitely be better off using dedicated software as suggested. But for a simple one, I'd advise to have a go yourself, as you will learn a lot. I've learnt so much since joining up here, I keep rewriting my code as there are always more efficient ways.
    1 point
  21. @LostKobrakai, I do use the @apeisa methode in combination with Inputfield Select File some times, it's very flexible.
    1 point
  22. Just to clarify, the zip file (files.zip) was correctly created in the /1062/ folder but the error mssg pointed to /1022/ which belongs to another page.
    1 point
  23. Hi there, I know I'm digging out a quite old topic, but I just stumbled across this in the processwire blog profile. As the webpages I code right now are often responsive with no real general page layout, exept header and footer part, but lot's of smaller repeated chunks of code (accordions, blocks), I was searching for a simple way of getting templates to work. Plain includes didn't really fit my needs, but a full templating engine seems quite overkill. It's sad that I just now found this class, while I know ProcessWire for over a year now. I think it would be nice to add a paragraph about this in the api section of the page, so it's more visible to new people as a option for generating markup.
    1 point
  24. You can find a detailed working example in the Skyscrapers Profile search form. It's a really detailed and well commented profile on how to work with several kinds of filtering.
    1 point
  25. Here's a live example of the Pagination Textformatter in action at CMS Critic: http://www.cmscritic.com/how-to-create-a-social-network/ I've also updated this module to version 2, which adds an API giving you further control over pagination.
    1 point
  26. How about a 301 with $session->redirect? Wouldn't that do the trick?
    1 point
  27. share my nginx config part for this: server { # all your stuff here error_page 404 /index.php?id=/http404/; # bla-bla-bla config # add this before location / {...} location ~ [^-_.a-zA-Z0-9/~] { try_files $uri $uri/ =404; } } working fine for me
    1 point
  28. To start with, last should be last() (I think) So $img = $m->images->last()->width(200); Then you need to get the url for the image <img src='{$img->url}' /> The URL contains all the path data.
    1 point
  29. Hi Alec, I would imagine that probably the easiest way to go here would be using GET parameters through $input->get https://processwire.com/api/variables/input/
    1 point
  30. @adrian thanks for your hard work! I tried the import omitting the repeater field an got the following response: That happened, when i tried to replace the content. After that I deleted alle the pages I already had in the import system and imported again. I got a similar message and no pages were imported. After a second try, some pages but not all got imported. It always displayed the »This page may not be deleted« message. When i tried »Append« instead of »Replace« the import seems to work, I got this screen: I hope this helps to narrow down the issue. Thanks again!
    1 point
  31. Thanks for sharing this with us. What you are saying I recognize happening the last 5 years in my own customer field, even similar prices you mentioned 300 - 350 - 1000 €. Not only in websites but in general also. I see more and more china shops popup and more people buying cheap china stuff. They care less for the quality shops. So I guess it's also part of a bad going economy. It's not something you can solve just by trying to find "better" clients, or telling them that if they want a cheap solution, they have to go to another website builder. On top of that there is a lot of competition in building websites. The bigger clients/companies won't ask for your services because they already know an established coder/designer. Other competition is a growing number of handy wordpress guys who quickly plug and play copy and paste something with a theme. Another problem is that some clients are impressed by how it looks on the outside and don't care much about what is going on in the back end. Hence the growing popularity of cheap trick wordpress. I think the old "divide and rule" will help something. Just add out of the box (templated) websites to your services. Tell the client if he goes for a cheap out of the box website, you can always add more needed functionality to it afterwards, payed of course. Just tell this very clearly in the beginning to your client, so there will be no "yes but . . . ." afterwards. Further you can use the experience and knowledge you already have not only for building websites for others but also for building and exploiting your own on-line shop.
    1 point
  32. @adrian sorry for the delay, i just saw your post. i tried to import the the data, but got the following error right after submitting the form: another thing: is it possible to point the importer to an already uploaded (e.g. via ftp) file instead of uploading the file via the form. i think most hosting environments will struggle with uploading large files. THANKS!
    1 point
  33. Hi Adrian, I'm happy to help, thanks. The folder C:/xampp/htdocs/mysite/site/assets/files/1022/migratorfiles is not there. The folder C:/xampp/htdocs/mysite/site/assets/files/1022/ contains some images that I uploaded for a specific page, a thumbnail and the main image. I don't understand why that folder was used to append the migratorfiles folder, maybe because it is the last folder listed in the in the $files array?
    1 point
  34. Hi Adrian, I managed to get this to work by comment out the following on line 2106: $valid_files = array(); if(is_array($files)) { foreach($files as $file) { //if(file_exists($file['currentpath']) || file_exists($file[0])) /* <-- This line */ $valid_files[] = $file; } } I've tried an import into another site and it works There may be implications I'm not aware of by doing this? Edit: Just tried with latest version 0.1.9 and that works too. Line 2106 in version 0.1.6 is now line 2144 in version 0.1.9 I did get this warning after the import to the new site both in versions 0.1.6 and 0.1.9: Warning: rmdir(C:/xampp/htdocs/mysite/site/assets/files/1022/migratorfiles): Directory not empty in C:\xampp\htdocs\mysite\site\modules\ProcessPageTreeMigrator\ProcessMigrator.module on line 2182
    1 point
  35. After finding the correct error_log file I reran this line and got the following: [29-Jun-2014 11:47:45 Europe/London] PHP Notice: Undefined offset: 0 in C:\xampp\htdocs\mysite\site\modules\ProcessPageTreeMigrator\ProcessMigrator.module on line 2106 [29-Jun-2014 11:47:45 Europe/London] PHP Notice: Undefined offset: 0 in C:\xampp\htdocs\mysite\site\modules\ProcessPageTreeMigrator\ProcessMigrator.module on line 2106 [29-Jun-2014 11:47:45 Europe/London] PHP Notice: Undefined offset: 0 in C:\xampp\htdocs\mysite\site\modules\ProcessPageTreeMigrator\ProcessMigrator.module on line 2106 [29-Jun-2014 11:47:45 Europe/London] PHP Notice: Undefined offset: 0 in C:\xampp\htdocs\mysite\site\modules\ProcessPageTreeMigrator\ProcessMigrator.module on line 2106 [29-Jun-2014 11:47:45 Europe/London] PHP Warning: Illegal string offset 'currentpath' in C:\xampp\htdocs\mysite\site\modules\ProcessPageTreeMigrator\ProcessMigrator.module on line 2106 And the same set of errors after running this after line 2108: if($filetype == 'json') error_log(print_r($valid_files, true));
    1 point
  36. This is so common - I do things like this all the time, not just for menus. Does anyone else think this should be part of the core? For now, I wrote a simple flat function: /** * Recursive traverse and visit every child in a sub-tree of Pages. * * @param Page $parent root Page from which to traverse * @param callable $enter function to call upon visiting a child Page * @param callable|null $exit function to call after visiting a child Page (and all of it's children) */ function visit(Page $parent, $enter, $exit=null) { foreach ($parent->children() as $child) { call_user_func($enter, $child); if ($child->numChildren > 0) { visit($child, $enter, $exit); } if ($exit) { call_user_func($exit, $child); } } } With PHP 5.3 you can generate a menu (or whatever else) recursively as simple as this: visit( $pages->get('/menus/main') , function(Page $page) { echo '<li><a href="' . $page->url . '">' . $page->title . '</a>'; if ($page->numChildren > 0) { echo '<ul>'; } } , function(Page $page) { echo '</li>'; if ($page->numChildren > 0) { echo '</ul>'; } } ); Dead simple. I've seen the options for modules etc. that generate menus - they seem to grow out of control with a million options, and my own helpers seem to evolve the same way, and it doesn't jive with the beautiful, self-contained, simple templates you normally get away with in PW. Would it make sense to have a standard visit() method in Page in the core?
    1 point
×
×
  • Create New...