Mercurial > p > roundup > code
comparison roundup/cgi/client.py @ 1818:85fd3d0e7d81
Actually use FormError, so we can move the handling up to inner_main().
Implement newItemAction using editItemAction, removing duplication.
| author | Johannes Gijsbers <jlgijsbers@users.sourceforge.net> |
|---|---|
| date | Wed, 24 Sep 2003 14:53:58 +0000 |
| parents | ea43cb3c157f |
| children | 4ac11e7fa11a |
comparison
equal
deleted
inserted
replaced
| 1817:3d180e08fae0 | 1818:85fd3d0e7d81 |
|---|---|
| 1 # $Id: client.py,v 1.139 2003-09-10 13:04:05 jlgijsbers Exp $ | 1 # $Id: client.py,v 1.140 2003-09-24 14:53:58 jlgijsbers Exp $ |
| 2 | 2 |
| 3 __doc__ = """ | 3 __doc__ = """ |
| 4 WWW request handler (also used in the stand-alone server). | 4 WWW request handler (also used in the stand-alone server). |
| 5 """ | 5 """ |
| 6 | 6 |
| 29 pass | 29 pass |
| 30 | 30 |
| 31 # used by a couple of routines | 31 # used by a couple of routines |
| 32 chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' | 32 chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' |
| 33 | 33 |
| 34 # XXX actually _use_ FormError | |
| 35 class FormError(ValueError): | 34 class FormError(ValueError): |
| 36 ''' An "expected" exception occurred during form parsing. | 35 ''' An "expected" exception occurred during form parsing. |
| 37 - ie. something we know can go wrong, and don't want to alarm the | 36 - ie. something we know can go wrong, and don't want to alarm the |
| 38 user with | 37 user with |
| 39 | 38 |
| 278 self.error_message.append(message) | 277 self.error_message.append(message) |
| 279 self.write(self.renderContext()) | 278 self.write(self.renderContext()) |
| 280 except NotFound: | 279 except NotFound: |
| 281 # pass through | 280 # pass through |
| 282 raise | 281 raise |
| 282 except FormError, e: | |
| 283 self.error_message.append(_('Form Error: ') + str(e)) | |
| 284 self.write(self.renderContext()) | |
| 283 except: | 285 except: |
| 284 # everything else | 286 # everything else |
| 285 self.write(cgitb.html()) | 287 self.write(cgitb.html()) |
| 286 | 288 |
| 287 def clean_sessions(self): | 289 def clean_sessions(self): |
| 724 '''Attempt to create a new user based on the contents of the form | 726 '''Attempt to create a new user based on the contents of the form |
| 725 and then set the cookie. | 727 and then set the cookie. |
| 726 | 728 |
| 727 return 1 on successful login | 729 return 1 on successful login |
| 728 ''' | 730 ''' |
| 729 # parse the props from the form | 731 props = self.parsePropsFromForm()[0][('user', None)] |
| 730 try: | |
| 731 props = self.parsePropsFromForm()[0][('user', None)] | |
| 732 except (ValueError, KeyError), message: | |
| 733 self.error_message.append(_('Error: ') + str(message)) | |
| 734 return | |
| 735 | 732 |
| 736 # make sure we're allowed to register | 733 # make sure we're allowed to register |
| 737 if not self.registerPermission(props): | 734 if not self.registerPermission(props): |
| 738 raise Unauthorised, _("You do not have permission to register") | 735 raise Unauthorised, _("You do not have permission to register") |
| 739 | 736 |
| 932 def editItemAction(self): | 929 def editItemAction(self): |
| 933 ''' Perform an edit of an item in the database. | 930 ''' Perform an edit of an item in the database. |
| 934 | 931 |
| 935 See parsePropsFromForm and _editnodes for special variables | 932 See parsePropsFromForm and _editnodes for special variables |
| 936 ''' | 933 ''' |
| 937 # parse the props from the form | 934 props, links = self.parsePropsFromForm() |
| 938 try: | |
| 939 props, links = self.parsePropsFromForm() | |
| 940 except (ValueError, KeyError), message: | |
| 941 self.error_message.append(_('Parse Error: ') + str(message)) | |
| 942 return | |
| 943 | 935 |
| 944 # handle the props | 936 # handle the props |
| 945 try: | 937 try: |
| 946 message = self._editnodes(props, links) | 938 message = self._editnodes(props, links) |
| 947 except (ValueError, KeyError, IndexError), message: | 939 except (ValueError, KeyError, IndexError), message: |
| 953 | 945 |
| 954 # redirect to the item's edit page | 946 # redirect to the item's edit page |
| 955 raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base, | 947 raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base, |
| 956 self.classname, self.nodeid, urllib.quote(message), | 948 self.classname, self.nodeid, urllib.quote(message), |
| 957 urllib.quote(self.template)) | 949 urllib.quote(self.template)) |
| 950 | |
| 951 newItemAction = editItemAction | |
| 958 | 952 |
| 959 def editItemPermission(self, props): | 953 def editItemPermission(self, props): |
| 960 ''' Determine whether the user has permission to edit this item. | 954 ''' Determine whether the user has permission to edit this item. |
| 961 | 955 |
| 962 Base behaviour is to check the user can edit this class. If we're | 956 Base behaviour is to check the user can edit this class. If we're |
| 977 if self.nodeid == self.userid: | 971 if self.nodeid == self.userid: |
| 978 return 1 | 972 return 1 |
| 979 if self.db.security.hasPermission('Edit', self.userid, self.classname): | 973 if self.db.security.hasPermission('Edit', self.userid, self.classname): |
| 980 return 1 | 974 return 1 |
| 981 return 0 | 975 return 0 |
| 982 | |
| 983 def newItemAction(self): | |
| 984 ''' Add a new item to the database. | |
| 985 | |
| 986 This follows the same form as the editItemAction, with the same | |
| 987 special form values. | |
| 988 ''' | |
| 989 # parse the props from the form | |
| 990 try: | |
| 991 props, links = self.parsePropsFromForm() | |
| 992 except (ValueError, KeyError), message: | |
| 993 self.error_message.append(_('Error: ') + str(message)) | |
| 994 return | |
| 995 | |
| 996 # handle the props - edit or create | |
| 997 try: | |
| 998 # when it hits the None element, it'll set self.nodeid | |
| 999 messages = self._editnodes(props, links) | |
| 1000 | |
| 1001 except (ValueError, KeyError, IndexError), message: | |
| 1002 # these errors might just be indicative of user dumbness | |
| 1003 self.error_message.append(_('Error: ') + str(message)) | |
| 1004 return | |
| 1005 | |
| 1006 # commit now that all the tricky stuff is done | |
| 1007 self.db.commit() | |
| 1008 | |
| 1009 # redirect to the new item's page | |
| 1010 raise Redirect, '%s%s%s?@ok_message=%s&@template=%s'%(self.base, | |
| 1011 self.classname, self.nodeid, urllib.quote(messages), | |
| 1012 urllib.quote(self.template)) | |
| 1013 | 976 |
| 1014 def newItemPermission(self, props): | 977 def newItemPermission(self, props): |
| 1015 ''' Determine whether the user has permission to create (edit) this | 978 ''' Determine whether the user has permission to create (edit) this |
| 1016 item. | 979 item. |
| 1017 | 980 |
| 1630 if d['link']: | 1593 if d['link']: |
| 1631 value = [] | 1594 value = [] |
| 1632 for entry in extractFormList(form[key]): | 1595 for entry in extractFormList(form[key]): |
| 1633 m = self.FV_DESIGNATOR.match(entry) | 1596 m = self.FV_DESIGNATOR.match(entry) |
| 1634 if not m: | 1597 if not m: |
| 1635 raise ValueError, \ | 1598 raise FormError, \ |
| 1636 'link "%s" value "%s" not a designator'%(key, entry) | 1599 'link "%s" value "%s" not a designator'%(key, entry) |
| 1637 value.append((m.group(1), m.group(2))) | 1600 value.append((m.group(1), m.group(2))) |
| 1638 | 1601 |
| 1639 # make sure the link property is valid | 1602 # make sure the link property is valid |
| 1640 if (not isinstance(propdef[propname], hyperdb.Multilink) and | 1603 if (not isinstance(propdef[propname], hyperdb.Multilink) and |
| 1641 not isinstance(propdef[propname], hyperdb.Link)): | 1604 not isinstance(propdef[propname], hyperdb.Link)): |
| 1642 raise ValueError, '%s %s is not a link or '\ | 1605 raise FormError, '%s %s is not a link or '\ |
| 1643 'multilink property'%(cn, propname) | 1606 'multilink property'%(cn, propname) |
| 1644 | 1607 |
| 1645 all_links.append((cn, nodeid, propname, value)) | 1608 all_links.append((cn, nodeid, propname, value)) |
| 1646 continue | 1609 continue |
| 1647 | 1610 |
| 1658 mlaction = 'add' | 1621 mlaction = 'add' |
| 1659 | 1622 |
| 1660 # does the property exist? | 1623 # does the property exist? |
| 1661 if not propdef.has_key(propname): | 1624 if not propdef.has_key(propname): |
| 1662 if mlaction != 'set': | 1625 if mlaction != 'set': |
| 1663 raise ValueError, 'You have submitted a %s action for'\ | 1626 raise FormError, 'You have submitted a %s action for'\ |
| 1664 ' the property "%s" which doesn\'t exist'%(mlaction, | 1627 ' the property "%s" which doesn\'t exist'%(mlaction, |
| 1665 propname) | 1628 propname) |
| 1666 # the form element is probably just something we don't care | 1629 # the form element is probably just something we don't care |
| 1667 # about - ignore it | 1630 # about - ignore it |
| 1668 continue | 1631 continue |
| 1676 if isinstance(proptype, hyperdb.Multilink): | 1639 if isinstance(proptype, hyperdb.Multilink): |
| 1677 value = extractFormList(value) | 1640 value = extractFormList(value) |
| 1678 else: | 1641 else: |
| 1679 # multiple values are not OK | 1642 # multiple values are not OK |
| 1680 if isinstance(value, type([])): | 1643 if isinstance(value, type([])): |
| 1681 raise ValueError, 'You have submitted more than one value'\ | 1644 raise FormError, 'You have submitted more than one value'\ |
| 1682 ' for the %s property'%propname | 1645 ' for the %s property'%propname |
| 1683 # value might be a file upload... | 1646 # value might be a file upload... |
| 1684 if not hasattr(value, 'filename') or value.filename is None: | 1647 if not hasattr(value, 'filename') or value.filename is None: |
| 1685 # nope, pull out the value and strip it | 1648 # nope, pull out the value and strip it |
| 1686 value = value.value.strip() | 1649 value = value.value.strip() |
| 1699 for key, d in matches: | 1662 for key, d in matches: |
| 1700 if d['confirm'] and d['propname'] == propname: | 1663 if d['confirm'] and d['propname'] == propname: |
| 1701 confirm = form[key] | 1664 confirm = form[key] |
| 1702 break | 1665 break |
| 1703 else: | 1666 else: |
| 1704 raise ValueError, 'Password and confirmation text do '\ | 1667 raise FormError, 'Password and confirmation text do '\ |
| 1705 'not match' | 1668 'not match' |
| 1706 if isinstance(confirm, type([])): | 1669 if isinstance(confirm, type([])): |
| 1707 raise ValueError, 'You have submitted more than one value'\ | 1670 raise FormError, 'You have submitted more than one value'\ |
| 1708 ' for the %s property'%propname | 1671 ' for the %s property'%propname |
| 1709 if value != confirm.value: | 1672 if value != confirm.value: |
| 1710 raise ValueError, 'Password and confirmation text do '\ | 1673 raise FormError, 'Password and confirmation text do '\ |
| 1711 'not match' | 1674 'not match' |
| 1712 value = password.Password(value) | 1675 value = password.Password(value) |
| 1713 | 1676 |
| 1714 elif isinstance(proptype, hyperdb.Link): | 1677 elif isinstance(proptype, hyperdb.Link): |
| 1715 # see if it's the "no selection" choice | 1678 # see if it's the "no selection" choice |
| 1723 link = proptype.classname | 1686 link = proptype.classname |
| 1724 if not num_re.match(value): | 1687 if not num_re.match(value): |
| 1725 try: | 1688 try: |
| 1726 value = db.classes[link].lookup(value) | 1689 value = db.classes[link].lookup(value) |
| 1727 except KeyError: | 1690 except KeyError: |
| 1728 raise ValueError, _('property "%(propname)s": ' | 1691 raise FormError, _('property "%(propname)s": ' |
| 1729 '%(value)s not a %(classname)s')%{ | 1692 '%(value)s not a %(classname)s')%{ |
| 1730 'propname': propname, 'value': value, | 1693 'propname': propname, 'value': value, |
| 1731 'classname': link} | 1694 'classname': link} |
| 1732 except TypeError, message: | 1695 except TypeError, message: |
| 1733 raise ValueError, _('you may only enter ID values ' | 1696 raise FormError, _('you may only enter ID values ' |
| 1734 'for property "%(propname)s": %(message)s')%{ | 1697 'for property "%(propname)s": %(message)s')%{ |
| 1735 'propname': propname, 'message': message} | 1698 'propname': propname, 'message': message} |
| 1736 elif isinstance(proptype, hyperdb.Multilink): | 1699 elif isinstance(proptype, hyperdb.Multilink): |
| 1737 # perform link class key value lookup if necessary | 1700 # perform link class key value lookup if necessary |
| 1738 link = proptype.classname | 1701 link = proptype.classname |
| 1742 if not entry: continue | 1705 if not entry: continue |
| 1743 if not num_re.match(entry): | 1706 if not num_re.match(entry): |
| 1744 try: | 1707 try: |
| 1745 entry = link_cl.lookup(entry) | 1708 entry = link_cl.lookup(entry) |
| 1746 except KeyError: | 1709 except KeyError: |
| 1747 raise ValueError, _('property "%(propname)s": ' | 1710 raise FormError, _('property "%(propname)s": ' |
| 1748 '"%(value)s" not an entry of %(classname)s')%{ | 1711 '"%(value)s" not an entry of %(classname)s')%{ |
| 1749 'propname': propname, 'value': entry, | 1712 'propname': propname, 'value': entry, |
| 1750 'classname': link} | 1713 'classname': link} |
| 1751 except TypeError, message: | 1714 except TypeError, message: |
| 1752 raise ValueError, _('you may only enter ID values ' | 1715 raise FormError, _('you may only enter ID values ' |
| 1753 'for property "%(propname)s": %(message)s')%{ | 1716 'for property "%(propname)s": %(message)s')%{ |
| 1754 'propname': propname, 'message': message} | 1717 'propname': propname, 'message': message} |
| 1755 l.append(entry) | 1718 l.append(entry) |
| 1756 l.sort() | 1719 l.sort() |
| 1757 | 1720 |
| 1773 # the list | 1736 # the list |
| 1774 for entry in l: | 1737 for entry in l: |
| 1775 try: | 1738 try: |
| 1776 existing.remove(entry) | 1739 existing.remove(entry) |
| 1777 except ValueError: | 1740 except ValueError: |
| 1778 raise ValueError, _('property "%(propname)s": ' | 1741 raise FormError, _('property "%(propname)s": ' |
| 1779 '"%(value)s" not currently in list')%{ | 1742 '"%(value)s" not currently in list')%{ |
| 1780 'propname': propname, 'value': entry} | 1743 'propname': propname, 'value': entry} |
| 1781 else: | 1744 else: |
| 1782 # add - easy, just don't dupe | 1745 # add - easy, just don't dupe |
| 1783 for entry in l: | 1746 for entry in l: |
| 1824 elif isinstance(proptype, hyperdb.Boolean): | 1787 elif isinstance(proptype, hyperdb.Boolean): |
| 1825 value = value.lower() in ('yes', 'true', 'on', '1') | 1788 value = value.lower() in ('yes', 'true', 'on', '1') |
| 1826 elif isinstance(proptype, hyperdb.Number): | 1789 elif isinstance(proptype, hyperdb.Number): |
| 1827 value = float(value) | 1790 value = float(value) |
| 1828 except ValueError, msg: | 1791 except ValueError, msg: |
| 1829 raise ValueError, _('Error with %s property: %s')%( | 1792 raise FormError, _('Error with %s property: %s')%( |
| 1830 propname, msg) | 1793 propname, msg) |
| 1831 | 1794 |
| 1832 # register that we got this property | 1795 # register that we got this property |
| 1833 if value: | 1796 if value: |
| 1834 got_props[this][propname] = 1 | 1797 got_props[this][propname] = 1 |
| 1840 except KeyError: | 1803 except KeyError: |
| 1841 # this might be a new property for which there is | 1804 # this might be a new property for which there is |
| 1842 # no existing value | 1805 # no existing value |
| 1843 if not propdef.has_key(propname): | 1806 if not propdef.has_key(propname): |
| 1844 raise | 1807 raise |
| 1808 except IndexError, message: | |
| 1809 raise FormError(str(message)) | |
| 1845 | 1810 |
| 1846 # make sure the existing multilink is sorted | 1811 # make sure the existing multilink is sorted |
| 1847 if isinstance(proptype, hyperdb.Multilink): | 1812 if isinstance(proptype, hyperdb.Multilink): |
| 1848 existing.sort() | 1813 existing.sort() |
| 1849 | 1814 |
| 1896 else: | 1861 else: |
| 1897 p = 'property' | 1862 p = 'property' |
| 1898 s.append('Required %s %s %s not supplied'%(thing[0], p, | 1863 s.append('Required %s %s %s not supplied'%(thing[0], p, |
| 1899 ', '.join(required))) | 1864 ', '.join(required))) |
| 1900 if s: | 1865 if s: |
| 1901 raise ValueError, '\n'.join(s) | 1866 raise FormError, '\n'.join(s) |
| 1902 | 1867 |
| 1903 # When creating a FileClass node, it should have a non-empty content | 1868 # When creating a FileClass node, it should have a non-empty content |
| 1904 # property to be created. When editing a FileClass node, it should | 1869 # property to be created. When editing a FileClass node, it should |
| 1905 # either have a non-empty content property or no property at all. In | 1870 # either have a non-empty content property or no property at all. In |
| 1906 # the latter case, nothing will change. | 1871 # the latter case, nothing will change. |
| 1908 if isinstance(self.db.classes[cn], hyperdb.FileClass): | 1873 if isinstance(self.db.classes[cn], hyperdb.FileClass): |
| 1909 if id == '-1': | 1874 if id == '-1': |
| 1910 if not props.get('content', ''): | 1875 if not props.get('content', ''): |
| 1911 del all_props[(cn, id)] | 1876 del all_props[(cn, id)] |
| 1912 elif props.has_key('content') and not props['content']: | 1877 elif props.has_key('content') and not props['content']: |
| 1913 raise ValueError, _('File is empty') | 1878 raise FormError, _('File is empty') |
| 1914 return all_props, all_links | 1879 return all_props, all_links |
| 1915 | 1880 |
| 1916 def fixNewlines(text): | 1881 def fixNewlines(text): |
| 1917 ''' Homogenise line endings. | 1882 ''' Homogenise line endings. |
| 1918 | 1883 |
