On the whole, 2016 has been a pretty quiet year for Cadre development, but there have been a few
Fixed flaky handling of permissions during function recursion. Now, all Octavia objects will get their userinfo members set before or during creation, and are completely independent of the root $myuser object. Calls to dynamic functions can now be reliably used to get permissions of the owner of the page. Previously, the behaviour was that dynamic function calls had the viewer's permissions (contrary to what it said on the entry creation page); now, the page's owner will be used instead. The dynamic function and page template may be owned by different people, but only the page's owner matters. Dynamic functions may freely recurse and call other pages, but only pages (of executability 0 through 3) affect the owner used. For example:
| –› function _B||owner of A|
| ––› function _B||owner of A|
| –––› function _C||owner of A|
| ––––› page D||owner of A|
| –––––› function _E||owner of D|
Note that, if page A and D are of executability 0 or executability 3 (or 1 and 2 in a mode other than 'view'), then this means the template author, who may be completely unrelated to these users, has control over the actual code that is being executed with the permissions of the owners of A and D at the behest of the viewer. (Make sure you trust the authors of your templates and dynamic functions before using them!)
Why the reason for the change? This makes it possible (and practical) to access private information in a controlled way. Taking the above example, the viewer may not be able to read page D, but the owner of A can. If all the pages in the example are owned by one person, then guessing games (such as the new NS Certification
site) can be implemented securely.
Added more fields to the Form Generator extension, particularly more HTML5 fields like email. I think form() is finally mature enough to be used in writing most edit modes.
Added a 'return to editing' link to the default 'submit' mode. (At long last.)
soft_age() now applies abs() to the difference between the two dates it is given before it generates a name, meaning that the order no longer matters. You'll still have to use another function to decide whether or not you should print "before" or "after", but it'll still be easier to make use of soft_age().