Created
January 17, 2010 20:38
-
-
Save zitterbewegung/279568 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [web2pyTM] admin | |
| site | |
| edit | |
| about | |
| errors | |
| logout | |
| help | |
| Error ticket for "sahana" | |
| Ticket 127.0.0.1.2010-01-17.14-35-27.94e7175f-1cdd-411e-af1e-aefc82fbee3e | |
| Error traceback | |
| 1. | |
| 2. | |
| 3. | |
| 4. | |
| 5. | |
| 6. | |
| 7. | |
| Traceback (most recent call last): | |
| File "gluon/restricted.py", line 173, in restricted | |
| File "/Applications/web2py.app/Contents/Resources/applications/sahana/models/01_RESTlike_controller.py", line 51, in <module> | |
| File "<string>", line 1, in <module> | |
| File "/Applications/web2py.app/Contents/Resources/applications/sahana/modules/s3xrc.py", line 49, in <module> | |
| ImportError: No module named cElementTree | |
| In file: /Applications/web2py.app/Contents/Resources/applications/sahana/models/01_RESTlike_controller.py | |
| 1. | |
| 2. | |
| 3. | |
| 4. | |
| 5. | |
| 6. | |
| 7. | |
| 8. | |
| 9. | |
| 10. | |
| 11. | |
| 12. | |
| 13. | |
| 14. | |
| 15. | |
| 16. | |
| 17. | |
| 18. | |
| 19. | |
| 20. | |
| 21. | |
| 22. | |
| 23. | |
| 24. | |
| 25. | |
| 26. | |
| 27. | |
| 28. | |
| 29. | |
| 30. | |
| 31. | |
| 32. | |
| 33. | |
| 34. | |
| 35. | |
| 36. | |
| 37. | |
| 38. | |
| 39. | |
| 40. | |
| 41. | |
| 42. | |
| 43. | |
| 44. | |
| 45. | |
| 46. | |
| 47. | |
| 48. | |
| 49. | |
| 50. | |
| 51. | |
| 52. | |
| 53. | |
| 54. | |
| 55. | |
| 56. | |
| 57. | |
| 58. | |
| 59. | |
| 60. | |
| 61. | |
| 62. | |
| 63. | |
| 64. | |
| 65. | |
| 66. | |
| 67. | |
| 68. | |
| 69. | |
| 70. | |
| 71. | |
| 72. | |
| 73. | |
| 74. | |
| 75. | |
| 76. | |
| 77. | |
| 78. | |
| 79. | |
| 80. | |
| 81. | |
| 82. | |
| 83. | |
| 84. | |
| 85. | |
| 86. | |
| 87. | |
| 88. | |
| 89. | |
| 90. | |
| 91. | |
| 92. | |
| 93. | |
| 94. | |
| 95. | |
| 96. | |
| 97. | |
| 98. | |
| 99. | |
| 100. | |
| 101. | |
| 102. | |
| 103. | |
| 104. | |
| 105. | |
| 106. | |
| 107. | |
| 108. | |
| 109. | |
| 110. | |
| 111. | |
| 112. | |
| 113. | |
| 114. | |
| 115. | |
| 116. | |
| 117. | |
| 118. | |
| 119. | |
| 120. | |
| 121. | |
| 122. | |
| 123. | |
| 124. | |
| 125. | |
| 126. | |
| 127. | |
| 128. | |
| 129. | |
| 130. | |
| 131. | |
| 132. | |
| 133. | |
| 134. | |
| 135. | |
| 136. | |
| 137. | |
| 138. | |
| 139. | |
| 140. | |
| 141. | |
| 142. | |
| 143. | |
| 144. | |
| 145. | |
| 146. | |
| 147. | |
| 148. | |
| 149. | |
| 150. | |
| 151. | |
| 152. | |
| 153. | |
| 154. | |
| 155. | |
| 156. | |
| 157. | |
| 158. | |
| 159. | |
| 160. | |
| 161. | |
| 162. | |
| 163. | |
| 164. | |
| 165. | |
| 166. | |
| 167. | |
| 168. | |
| 169. | |
| 170. | |
| 171. | |
| 172. | |
| 173. | |
| 174. | |
| 175. | |
| 176. | |
| 177. | |
| 178. | |
| 179. | |
| 180. | |
| 181. | |
| 182. | |
| 183. | |
| 184. | |
| 185. | |
| 186. | |
| 187. | |
| 188. | |
| 189. | |
| 190. | |
| 191. | |
| 192. | |
| 193. | |
| 194. | |
| 195. | |
| 196. | |
| 197. | |
| 198. | |
| 199. | |
| 200. | |
| 201. | |
| 202. | |
| 203. | |
| 204. | |
| 205. | |
| 206. | |
| 207. | |
| 208. | |
| 209. | |
| 210. | |
| 211. | |
| 212. | |
| 213. | |
| 214. | |
| 215. | |
| 216. | |
| 217. | |
| 218. | |
| 219. | |
| 220. | |
| 221. | |
| 222. | |
| 223. | |
| 224. | |
| 225. | |
| 226. | |
| 227. | |
| 228. | |
| 229. | |
| 230. | |
| 231. | |
| 232. | |
| 233. | |
| 234. | |
| 235. | |
| 236. | |
| 237. | |
| 238. | |
| 239. | |
| 240. | |
| 241. | |
| 242. | |
| 243. | |
| 244. | |
| 245. | |
| 246. | |
| 247. | |
| 248. | |
| 249. | |
| 250. | |
| 251. | |
| 252. | |
| 253. | |
| 254. | |
| 255. | |
| 256. | |
| 257. | |
| 258. | |
| 259. | |
| 260. | |
| 261. | |
| 262. | |
| 263. | |
| 264. | |
| 265. | |
| 266. | |
| 267. | |
| 268. | |
| 269. | |
| 270. | |
| 271. | |
| 272. | |
| 273. | |
| 274. | |
| 275. | |
| 276. | |
| 277. | |
| 278. | |
| 279. | |
| 280. | |
| 281. | |
| 282. | |
| 283. | |
| 284. | |
| 285. | |
| 286. | |
| 287. | |
| 288. | |
| 289. | |
| 290. | |
| 291. | |
| 292. | |
| 293. | |
| 294. | |
| 295. | |
| 296. | |
| 297. | |
| 298. | |
| 299. | |
| 300. | |
| 301. | |
| 302. | |
| 303. | |
| 304. | |
| 305. | |
| 306. | |
| 307. | |
| 308. | |
| 309. | |
| 310. | |
| 311. | |
| 312. | |
| 313. | |
| 314. | |
| 315. | |
| 316. | |
| 317. | |
| 318. | |
| 319. | |
| 320. | |
| 321. | |
| 322. | |
| 323. | |
| 324. | |
| 325. | |
| 326. | |
| 327. | |
| 328. | |
| 329. | |
| 330. | |
| 331. | |
| 332. | |
| 333. | |
| 334. | |
| 335. | |
| 336. | |
| 337. | |
| 338. | |
| 339. | |
| 340. | |
| 341. | |
| 342. | |
| 343. | |
| 344. | |
| 345. | |
| 346. | |
| 347. | |
| 348. | |
| 349. | |
| 350. | |
| 351. | |
| 352. | |
| 353. | |
| 354. | |
| 355. | |
| 356. | |
| 357. | |
| 358. | |
| 359. | |
| 360. | |
| 361. | |
| 362. | |
| 363. | |
| 364. | |
| 365. | |
| 366. | |
| 367. | |
| 368. | |
| 369. | |
| 370. | |
| 371. | |
| 372. | |
| 373. | |
| 374. | |
| 375. | |
| 376. | |
| 377. | |
| 378. | |
| 379. | |
| 380. | |
| 381. | |
| 382. | |
| 383. | |
| 384. | |
| 385. | |
| 386. | |
| 387. | |
| 388. | |
| 389. | |
| 390. | |
| 391. | |
| 392. | |
| 393. | |
| 394. | |
| 395. | |
| 396. | |
| 397. | |
| 398. | |
| 399. | |
| 400. | |
| 401. | |
| 402. | |
| 403. | |
| 404. | |
| 405. | |
| 406. | |
| 407. | |
| 408. | |
| 409. | |
| 410. | |
| 411. | |
| 412. | |
| 413. | |
| 414. | |
| 415. | |
| 416. | |
| 417. | |
| 418. | |
| 419. | |
| 420. | |
| 421. | |
| 422. | |
| 423. | |
| 424. | |
| 425. | |
| 426. | |
| 427. | |
| 428. | |
| 429. | |
| 430. | |
| 431. | |
| 432. | |
| 433. | |
| 434. | |
| 435. | |
| 436. | |
| 437. | |
| 438. | |
| 439. | |
| 440. | |
| 441. | |
| 442. | |
| 443. | |
| 444. | |
| 445. | |
| 446. | |
| 447. | |
| 448. | |
| 449. | |
| 450. | |
| 451. | |
| 452. | |
| 453. | |
| 454. | |
| 455. | |
| 456. | |
| 457. | |
| 458. | |
| 459. | |
| 460. | |
| 461. | |
| 462. | |
| 463. | |
| 464. | |
| 465. | |
| 466. | |
| 467. | |
| 468. | |
| 469. | |
| 470. | |
| 471. | |
| 472. | |
| 473. | |
| 474. | |
| 475. | |
| 476. | |
| 477. | |
| 478. | |
| 479. | |
| 480. | |
| 481. | |
| 482. | |
| 483. | |
| 484. | |
| 485. | |
| 486. | |
| 487. | |
| 488. | |
| 489. | |
| 490. | |
| 491. | |
| 492. | |
| 493. | |
| 494. | |
| 495. | |
| 496. | |
| 497. | |
| 498. | |
| 499. | |
| 500. | |
| 501. | |
| 502. | |
| 503. | |
| 504. | |
| 505. | |
| 506. | |
| 507. | |
| 508. | |
| 509. | |
| 510. | |
| 511. | |
| 512. | |
| 513. | |
| 514. | |
| 515. | |
| 516. | |
| 517. | |
| 518. | |
| 519. | |
| 520. | |
| 521. | |
| 522. | |
| 523. | |
| 524. | |
| 525. | |
| 526. | |
| 527. | |
| 528. | |
| 529. | |
| 530. | |
| 531. | |
| 532. | |
| 533. | |
| 534. | |
| 535. | |
| 536. | |
| 537. | |
| 538. | |
| 539. | |
| 540. | |
| 541. | |
| 542. | |
| 543. | |
| 544. | |
| 545. | |
| 546. | |
| 547. | |
| 548. | |
| 549. | |
| 550. | |
| 551. | |
| 552. | |
| 553. | |
| 554. | |
| 555. | |
| 556. | |
| 557. | |
| 558. | |
| 559. | |
| 560. | |
| 561. | |
| 562. | |
| 563. | |
| 564. | |
| 565. | |
| 566. | |
| 567. | |
| 568. | |
| 569. | |
| 570. | |
| 571. | |
| 572. | |
| 573. | |
| 574. | |
| 575. | |
| 576. | |
| 577. | |
| 578. | |
| 579. | |
| 580. | |
| 581. | |
| 582. | |
| 583. | |
| 584. | |
| 585. | |
| 586. | |
| 587. | |
| 588. | |
| 589. | |
| 590. | |
| 591. | |
| 592. | |
| 593. | |
| 594. | |
| 595. | |
| 596. | |
| 597. | |
| 598. | |
| 599. | |
| 600. | |
| 601. | |
| 602. | |
| 603. | |
| 604. | |
| 605. | |
| 606. | |
| 607. | |
| 608. | |
| 609. | |
| 610. | |
| 611. | |
| 612. | |
| 613. | |
| 614. | |
| 615. | |
| 616. | |
| 617. | |
| 618. | |
| 619. | |
| 620. | |
| 621. | |
| 622. | |
| 623. | |
| 624. | |
| 625. | |
| 626. | |
| 627. | |
| 628. | |
| 629. | |
| 630. | |
| 631. | |
| 632. | |
| 633. | |
| 634. | |
| 635. | |
| 636. | |
| 637. | |
| 638. | |
| 639. | |
| 640. | |
| 641. | |
| 642. | |
| 643. | |
| 644. | |
| 645. | |
| 646. | |
| 647. | |
| 648. | |
| 649. | |
| 650. | |
| 651. | |
| 652. | |
| 653. | |
| 654. | |
| 655. | |
| 656. | |
| 657. | |
| 658. | |
| 659. | |
| 660. | |
| 661. | |
| 662. | |
| 663. | |
| 664. | |
| 665. | |
| 666. | |
| 667. | |
| 668. | |
| 669. | |
| 670. | |
| 671. | |
| 672. | |
| 673. | |
| 674. | |
| 675. | |
| 676. | |
| 677. | |
| 678. | |
| 679. | |
| 680. | |
| 681. | |
| 682. | |
| 683. | |
| 684. | |
| 685. | |
| 686. | |
| 687. | |
| 688. | |
| 689. | |
| 690. | |
| 691. | |
| 692. | |
| 693. | |
| 694. | |
| 695. | |
| 696. | |
| 697. | |
| 698. | |
| 699. | |
| 700. | |
| 701. | |
| 702. | |
| 703. | |
| 704. | |
| 705. | |
| 706. | |
| 707. | |
| 708. | |
| 709. | |
| 710. | |
| 711. | |
| 712. | |
| 713. | |
| 714. | |
| 715. | |
| 716. | |
| 717. | |
| 718. | |
| 719. | |
| 720. | |
| 721. | |
| 722. | |
| 723. | |
| 724. | |
| 725. | |
| 726. | |
| 727. | |
| 728. | |
| 729. | |
| 730. | |
| 731. | |
| 732. | |
| 733. | |
| 734. | |
| 735. | |
| 736. | |
| 737. | |
| 738. | |
| 739. | |
| 740. | |
| 741. | |
| 742. | |
| 743. | |
| 744. | |
| 745. | |
| 746. | |
| 747. | |
| 748. | |
| 749. | |
| 750. | |
| 751. | |
| 752. | |
| 753. | |
| 754. | |
| 755. | |
| 756. | |
| 757. | |
| 758. | |
| 759. | |
| 760. | |
| 761. | |
| 762. | |
| 763. | |
| 764. | |
| 765. | |
| 766. | |
| 767. | |
| 768. | |
| 769. | |
| 770. | |
| 771. | |
| 772. | |
| 773. | |
| 774. | |
| 775. | |
| 776. | |
| 777. | |
| 778. | |
| 779. | |
| 780. | |
| 781. | |
| 782. | |
| 783. | |
| 784. | |
| 785. | |
| 786. | |
| 787. | |
| 788. | |
| 789. | |
| 790. | |
| 791. | |
| 792. | |
| 793. | |
| 794. | |
| 795. | |
| 796. | |
| 797. | |
| 798. | |
| 799. | |
| 800. | |
| 801. | |
| 802. | |
| 803. | |
| 804. | |
| 805. | |
| 806. | |
| 807. | |
| 808. | |
| 809. | |
| 810. | |
| 811. | |
| 812. | |
| 813. | |
| 814. | |
| 815. | |
| 816. | |
| 817. | |
| 818. | |
| 819. | |
| 820. | |
| 821. | |
| 822. | |
| 823. | |
| 824. | |
| 825. | |
| 826. | |
| 827. | |
| 828. | |
| 829. | |
| 830. | |
| 831. | |
| 832. | |
| 833. | |
| 834. | |
| 835. | |
| 836. | |
| 837. | |
| 838. | |
| 839. | |
| 840. | |
| 841. | |
| 842. | |
| 843. | |
| 844. | |
| 845. | |
| 846. | |
| 847. | |
| 848. | |
| 849. | |
| 850. | |
| 851. | |
| 852. | |
| 853. | |
| 854. | |
| 855. | |
| 856. | |
| 857. | |
| 858. | |
| 859. | |
| 860. | |
| 861. | |
| 862. | |
| 863. | |
| 864. | |
| 865. | |
| 866. | |
| 867. | |
| 868. | |
| 869. | |
| 870. | |
| 871. | |
| 872. | |
| 873. | |
| 874. | |
| 875. | |
| 876. | |
| 877. | |
| 878. | |
| 879. | |
| 880. | |
| 881. | |
| 882. | |
| 883. | |
| 884. | |
| 885. | |
| 886. | |
| 887. | |
| 888. | |
| 889. | |
| 890. | |
| 891. | |
| 892. | |
| 893. | |
| 894. | |
| 895. | |
| 896. | |
| 897. | |
| 898. | |
| 899. | |
| 900. | |
| 901. | |
| 902. | |
| 903. | |
| 904. | |
| 905. | |
| 906. | |
| 907. | |
| 908. | |
| 909. | |
| 910. | |
| 911. | |
| 912. | |
| 913. | |
| 914. | |
| 915. | |
| 916. | |
| 917. | |
| 918. | |
| 919. | |
| 920. | |
| 921. | |
| 922. | |
| 923. | |
| 924. | |
| 925. | |
| 926. | |
| 927. | |
| 928. | |
| 929. | |
| 930. | |
| 931. | |
| 932. | |
| 933. | |
| 934. | |
| 935. | |
| 936. | |
| 937. | |
| 938. | |
| 939. | |
| 940. | |
| 941. | |
| 942. | |
| 943. | |
| 944. | |
| 945. | |
| 946. | |
| 947. | |
| 948. | |
| 949. | |
| 950. | |
| 951. | |
| 952. | |
| 953. | |
| 954. | |
| 955. | |
| 956. | |
| 957. | |
| 958. | |
| 959. | |
| 960. | |
| 961. | |
| 962. | |
| 963. | |
| 964. | |
| 965. | |
| 966. | |
| 967. | |
| 968. | |
| 969. | |
| 970. | |
| 971. | |
| 972. | |
| 973. | |
| 974. | |
| 975. | |
| 976. | |
| 977. | |
| 978. | |
| 979. | |
| 980. | |
| 981. | |
| 982. | |
| 983. | |
| 984. | |
| 985. | |
| 986. | |
| 987. | |
| 988. | |
| 989. | |
| 990. | |
| 991. | |
| 992. | |
| 993. | |
| 994. | |
| 995. | |
| 996. | |
| 997. | |
| 998. | |
| 999. | |
| 1000. | |
| 1001. | |
| 1002. | |
| 1003. | |
| 1004. | |
| 1005. | |
| 1006. | |
| 1007. | |
| 1008. | |
| 1009. | |
| 1010. | |
| 1011. | |
| 1012. | |
| 1013. | |
| 1014. | |
| 1015. | |
| 1016. | |
| 1017. | |
| 1018. | |
| 1019. | |
| 1020. | |
| 1021. | |
| 1022. | |
| 1023. | |
| 1024. | |
| 1025. | |
| 1026. | |
| 1027. | |
| 1028. | |
| 1029. | |
| 1030. | |
| 1031. | |
| 1032. | |
| 1033. | |
| 1034. | |
| 1035. | |
| 1036. | |
| 1037. | |
| 1038. | |
| 1039. | |
| 1040. | |
| 1041. | |
| 1042. | |
| 1043. | |
| 1044. | |
| 1045. | |
| 1046. | |
| 1047. | |
| 1048. | |
| 1049. | |
| 1050. | |
| 1051. | |
| 1052. | |
| 1053. | |
| 1054. | |
| 1055. | |
| 1056. | |
| 1057. | |
| 1058. | |
| 1059. | |
| 1060. | |
| 1061. | |
| 1062. | |
| 1063. | |
| 1064. | |
| 1065. | |
| 1066. | |
| 1067. | |
| 1068. | |
| 1069. | |
| 1070. | |
| 1071. | |
| 1072. | |
| 1073. | |
| 1074. | |
| 1075. | |
| 1076. | |
| 1077. | |
| 1078. | |
| 1079. | |
| 1080. | |
| 1081. | |
| 1082. | |
| 1083. | |
| 1084. | |
| 1085. | |
| 1086. | |
| 1087. | |
| 1088. | |
| 1089. | |
| 1090. | |
| 1091. | |
| 1092. | |
| 1093. | |
| 1094. | |
| 1095. | |
| 1096. | |
| 1097. | |
| 1098. | |
| 1099. | |
| 1100. | |
| 1101. | |
| 1102. | |
| 1103. | |
| 1104. | |
| 1105. | |
| 1106. | |
| 1107. | |
| 1108. | |
| 1109. | |
| 1110. | |
| 1111. | |
| 1112. | |
| 1113. | |
| 1114. | |
| 1115. | |
| 1116. | |
| 1117. | |
| 1118. | |
| 1119. | |
| 1120. | |
| 1121. | |
| 1122. | |
| 1123. | |
| 1124. | |
| 1125. | |
| 1126. | |
| 1127. | |
| 1128. | |
| 1129. | |
| 1130. | |
| 1131. | |
| 1132. | |
| 1133. | |
| 1134. | |
| 1135. | |
| 1136. | |
| 1137. | |
| 1138. | |
| 1139. | |
| 1140. | |
| 1141. | |
| 1142. | |
| 1143. | |
| 1144. | |
| 1145. | |
| 1146. | |
| 1147. | |
| 1148. | |
| 1149. | |
| 1150. | |
| 1151. | |
| 1152. | |
| 1153. | |
| 1154. | |
| 1155. | |
| 1156. | |
| 1157. | |
| 1158. | |
| 1159. | |
| 1160. | |
| 1161. | |
| 1162. | |
| 1163. | |
| 1164. | |
| 1165. | |
| 1166. | |
| 1167. | |
| 1168. | |
| 1169. | |
| 1170. | |
| 1171. | |
| 1172. | |
| 1173. | |
| 1174. | |
| 1175. | |
| 1176. | |
| 1177. | |
| 1178. | |
| 1179. | |
| 1180. | |
| 1181. | |
| 1182. | |
| 1183. | |
| 1184. | |
| 1185. | |
| 1186. | |
| 1187. | |
| 1188. | |
| 1189. | |
| 1190. | |
| 1191. | |
| 1192. | |
| 1193. | |
| 1194. | |
| 1195. | |
| 1196. | |
| 1197. | |
| 1198. | |
| 1199. | |
| 1200. | |
| 1201. | |
| 1202. | |
| 1203. | |
| 1204. | |
| 1205. | |
| 1206. | |
| 1207. | |
| 1208. | |
| 1209. | |
| 1210. | |
| 1211. | |
| 1212. | |
| 1213. | |
| 1214. | |
| 1215. | |
| 1216. | |
| 1217. | |
| 1218. | |
| 1219. | |
| 1220. | |
| 1221. | |
| 1222. | |
| 1223. | |
| 1224. | |
| 1225. | |
| 1226. | |
| 1227. | |
| 1228. | |
| 1229. | |
| 1230. | |
| 1231. | |
| 1232. | |
| 1233. | |
| 1234. | |
| 1235. | |
| 1236. | |
| 1237. | |
| 1238. | |
| 1239. | |
| 1240. | |
| 1241. | |
| 1242. | |
| 1243. | |
| 1244. | |
| 1245. | |
| 1246. | |
| 1247. | |
| 1248. | |
| 1249. | |
| 1250. | |
| 1251. | |
| 1252. | |
| 1253. | |
| 1254. | |
| 1255. | |
| 1256. | |
| 1257. | |
| 1258. | |
| 1259. | |
| 1260. | |
| 1261. | |
| 1262. | |
| 1263. | |
| 1264. | |
| 1265. | |
| 1266. | |
| 1267. | |
| 1268. | |
| 1269. | |
| 1270. | |
| 1271. | |
| 1272. | |
| 1273. | |
| 1274. | |
| 1275. | |
| 1276. | |
| 1277. | |
| 1278. | |
| 1279. | |
| 1280. | |
| 1281. | |
| 1282. | |
| 1283. | |
| 1284. | |
| 1285. | |
| 1286. | |
| 1287. | |
| 1288. | |
| 1289. | |
| 1290. | |
| 1291. | |
| 1292. | |
| 1293. | |
| 1294. | |
| 1295. | |
| 1296. | |
| 1297. | |
| 1298. | |
| 1299. | |
| 1300. | |
| 1301. | |
| 1302. | |
| 1303. | |
| 1304. | |
| 1305. | |
| 1306. | |
| 1307. | |
| 1308. | |
| 1309. | |
| 1310. | |
| 1311. | |
| 1312. | |
| 1313. | |
| 1314. | |
| 1315. | |
| 1316. | |
| 1317. | |
| 1318. | |
| 1319. | |
| 1320. | |
| 1321. | |
| 1322. | |
| 1323. | |
| 1324. | |
| 1325. | |
| 1326. | |
| 1327. | |
| 1328. | |
| 1329. | |
| 1330. | |
| 1331. | |
| 1332. | |
| 1333. | |
| 1334. | |
| 1335. | |
| 1336. | |
| 1337. | |
| 1338. | |
| 1339. | |
| 1340. | |
| 1341. | |
| 1342. | |
| 1343. | |
| 1344. | |
| 1345. | |
| 1346. | |
| 1347. | |
| 1348. | |
| 1349. | |
| 1350. | |
| 1351. | |
| 1352. | |
| 1353. | |
| 1354. | |
| 1355. | |
| 1356. | |
| 1357. | |
| 1358. | |
| 1359. | |
| 1360. | |
| 1361. | |
| 1362. | |
| 1363. | |
| 1364. | |
| 1365. | |
| 1366. | |
| 1367. | |
| 1368. | |
| 1369. | |
| 1370. | |
| 1371. | |
| 1372. | |
| 1373. | |
| 1374. | |
| 1375. | |
| 1376. | |
| 1377. | |
| 1378. | |
| 1379. | |
| 1380. | |
| 1381. | |
| 1382. | |
| 1383. | |
| 1384. | |
| 1385. | |
| 1386. | |
| 1387. | |
| 1388. | |
| 1389. | |
| 1390. | |
| 1391. | |
| 1392. | |
| 1393. | |
| 1394. | |
| 1395. | |
| 1396. | |
| 1397. | |
| 1398. | |
| 1399. | |
| 1400. | |
| 1401. | |
| 1402. | |
| 1403. | |
| 1404. | |
| 1405. | |
| 1406. | |
| 1407. | |
| 1408. | |
| 1409. | |
| 1410. | |
| 1411. | |
| 1412. | |
| 1413. | |
| 1414. | |
| 1415. | |
| 1416. | |
| 1417. | |
| 1418. | |
| 1419. | |
| 1420. | |
| 1421. | |
| 1422. | |
| 1423. | |
| 1424. | |
| 1425. | |
| 1426. | |
| 1427. | |
| 1428. | |
| 1429. | |
| 1430. | |
| 1431. | |
| 1432. | |
| 1433. | |
| 1434. | |
| 1435. | |
| 1436. | |
| 1437. | |
| 1438. | |
| 1439. | |
| 1440. | |
| 1441. | |
| 1442. | |
| 1443. | |
| 1444. | |
| 1445. | |
| 1446. | |
| 1447. | |
| 1448. | |
| 1449. | |
| 1450. | |
| 1451. | |
| 1452. | |
| 1453. | |
| 1454. | |
| 1455. | |
| 1456. | |
| 1457. | |
| 1458. | |
| 1459. | |
| 1460. | |
| 1461. | |
| 1462. | |
| 1463. | |
| 1464. | |
| 1465. | |
| 1466. | |
| 1467. | |
| 1468. | |
| 1469. | |
| 1470. | |
| 1471. | |
| 1472. | |
| 1473. | |
| 1474. | |
| 1475. | |
| 1476. | |
| 1477. | |
| 1478. | |
| 1479. | |
| 1480. | |
| 1481. | |
| 1482. | |
| 1483. | |
| 1484. | |
| 1485. | |
| 1486. | |
| 1487. | |
| 1488. | |
| 1489. | |
| 1490. | |
| 1491. | |
| 1492. | |
| 1493. | |
| 1494. | |
| 1495. | |
| 1496. | |
| 1497. | |
| 1498. | |
| 1499. | |
| 1500. | |
| 1501. | |
| 1502. | |
| 1503. | |
| 1504. | |
| 1505. | |
| 1506. | |
| 1507. | |
| 1508. | |
| 1509. | |
| 1510. | |
| 1511. | |
| 1512. | |
| 1513. | |
| 1514. | |
| 1515. | |
| 1516. | |
| 1517. | |
| 1518. | |
| 1519. | |
| 1520. | |
| 1521. | |
| 1522. | |
| 1523. | |
| 1524. | |
| 1525. | |
| 1526. | |
| 1527. | |
| 1528. | |
| 1529. | |
| 1530. | |
| 1531. | |
| 1532. | |
| 1533. | |
| 1534. | |
| 1535. | |
| 1536. | |
| 1537. | |
| 1538. | |
| 1539. | |
| 1540. | |
| 1541. | |
| 1542. | |
| 1543. | |
| 1544. | |
| 1545. | |
| 1546. | |
| 1547. | |
| 1548. | |
| 1549. | |
| 1550. | |
| 1551. | |
| 1552. | |
| 1553. | |
| 1554. | |
| 1555. | |
| 1556. | |
| 1557. | |
| 1558. | |
| 1559. | |
| 1560. | |
| 1561. | |
| 1562. | |
| 1563. | |
| 1564. | |
| 1565. | |
| 1566. | |
| 1567. | |
| 1568. | |
| 1569. | |
| 1570. | |
| 1571. | |
| 1572. | |
| 1573. | |
| 1574. | |
| 1575. | |
| 1576. | |
| 1577. | |
| 1578. | |
| 1579. | |
| 1580. | |
| 1581. | |
| 1582. | |
| 1583. | |
| 1584. | |
| 1585. | |
| 1586. | |
| 1587. | |
| 1588. | |
| 1589. | |
| 1590. | |
| 1591. | |
| 1592. | |
| 1593. | |
| 1594. | |
| 1595. | |
| 1596. | |
| 1597. | |
| 1598. | |
| 1599. | |
| 1600. | |
| 1601. | |
| 1602. | |
| 1603. | |
| 1604. | |
| 1605. | |
| 1606. | |
| 1607. | |
| 1608. | |
| 1609. | |
| 1610. | |
| 1611. | |
| 1612. | |
| 1613. | |
| 1614. | |
| 1615. | |
| 1616. | |
| 1617. | |
| 1618. | |
| 1619. | |
| 1620. | |
| 1621. | |
| 1622. | |
| 1623. | |
| 1624. | |
| 1625. | |
| 1626. | |
| 1627. | |
| 1628. | |
| 1629. | |
| 1630. | |
| 1631. | |
| 1632. | |
| 1633. | |
| 1634. | |
| 1635. | |
| 1636. | |
| 1637. | |
| 1638. | |
| 1639. | |
| 1640. | |
| 1641. | |
| 1642. | |
| 1643. | |
| 1644. | |
| 1645. | |
| 1646. | |
| 1647. | |
| 1648. | |
| 1649. | |
| 1650. | |
| 1651. | |
| 1652. | |
| 1653. | |
| 1654. | |
| 1655. | |
| 1656. | |
| 1657. | |
| 1658. | |
| 1659. | |
| 1660. | |
| 1661. | |
| 1662. | |
| 1663. | |
| 1664. | |
| 1665. | |
| 1666. | |
| 1667. | |
| 1668. | |
| 1669. | |
| 1670. | |
| 1671. | |
| 1672. | |
| 1673. | |
| 1674. | |
| 1675. | |
| 1676. | |
| 1677. | |
| 1678. | |
| 1679. | |
| 1680. | |
| 1681. | |
| 1682. | |
| 1683. | |
| 1684. | |
| 1685. | |
| 1686. | |
| 1687. | |
| 1688. | |
| 1689. | |
| 1690. | |
| 1691. | |
| 1692. | |
| 1693. | |
| 1694. | |
| 1695. | |
| 1696. | |
| 1697. | |
| 1698. | |
| 1699. | |
| 1700. | |
| 1701. | |
| 1702. | |
| 1703. | |
| 1704. | |
| 1705. | |
| 1706. | |
| 1707. | |
| 1708. | |
| 1709. | |
| 1710. | |
| 1711. | |
| 1712. | |
| 1713. | |
| 1714. | |
| 1715. | |
| 1716. | |
| 1717. | |
| 1718. | |
| 1719. | |
| 1720. | |
| 1721. | |
| 1722. | |
| 1723. | |
| 1724. | |
| 1725. | |
| 1726. | |
| 1727. | |
| 1728. | |
| 1729. | |
| 1730. | |
| 1731. | |
| 1732. | |
| 1733. | |
| 1734. | |
| 1735. | |
| 1736. | |
| 1737. | |
| 1738. | |
| 1739. | |
| 1740. | |
| 1741. | |
| 1742. | |
| 1743. | |
| 1744. | |
| 1745. | |
| 1746. | |
| 1747. | |
| 1748. | |
| 1749. | |
| 1750. | |
| 1751. | |
| 1752. | |
| 1753. | |
| 1754. | |
| 1755. | |
| 1756. | |
| 1757. | |
| 1758. | |
| 1759. | |
| 1760. | |
| 1761. | |
| 1762. | |
| 1763. | |
| 1764. | |
| 1765. | |
| 1766. | |
| 1767. | |
| 1768. | |
| 1769. | |
| 1770. | |
| 1771. | |
| 1772. | |
| 1773. | |
| 1774. | |
| 1775. | |
| 1776. | |
| 1777. | |
| 1778. | |
| 1779. | |
| 1780. | |
| 1781. | |
| 1782. | |
| 1783. | |
| 1784. | |
| 1785. | |
| 1786. | |
| 1787. | |
| 1788. | |
| 1789. | |
| 1790. | |
| 1791. | |
| 1792. | |
| 1793. | |
| 1794. | |
| 1795. | |
| 1796. | |
| 1797. | |
| 1798. | |
| 1799. | |
| 1800. | |
| 1801. | |
| 1802. | |
| 1803. | |
| 1804. | |
| 1805. | |
| 1806. | |
| 1807. | |
| 1808. | |
| 1809. | |
| 1810. | |
| 1811. | |
| 1812. | |
| 1813. | |
| 1814. | |
| 1815. | |
| 1816. | |
| 1817. | |
| 1818. | |
| 1819. | |
| 1820. | |
| 1821. | |
| 1822. | |
| 1823. | |
| 1824. | |
| 1825. | |
| 1826. | |
| 1827. | |
| 1828. | |
| 1829. | |
| 1830. | |
| 1831. | |
| 1832. | |
| 1833. | |
| 1834. | |
| 1835. | |
| 1836. | |
| 1837. | |
| 1838. | |
| 1839. | |
| 1840. | |
| 1841. | |
| 1842. | |
| 1843. | |
| 1844. | |
| 1845. | |
| 1846. | |
| 1847. | |
| 1848. | |
| 1849. | |
| 1850. | |
| 1851. | |
| 1852. | |
| 1853. | |
| 1854. | |
| 1855. | |
| 1856. | |
| 1857. | |
| 1858. | |
| 1859. | |
| 1860. | |
| 1861. | |
| 1862. | |
| 1863. | |
| 1864. | |
| 1865. | |
| 1866. | |
| 1867. | |
| 1868. | |
| 1869. | |
| 1870. | |
| 1871. | |
| 1872. | |
| 1873. | |
| 1874. | |
| 1875. | |
| 1876. | |
| 1877. | |
| 1878. | |
| 1879. | |
| 1880. | |
| 1881. | |
| 1882. | |
| 1883. | |
| 1884. | |
| 1885. | |
| 1886. | |
| 1887. | |
| 1888. | |
| 1889. | |
| 1890. | |
| 1891. | |
| 1892. | |
| 1893. | |
| 1894. | |
| 1895. | |
| 1896. | |
| 1897. | |
| 1898. | |
| 1899. | |
| 1900. | |
| 1901. | |
| 1902. | |
| 1903. | |
| 1904. | |
| 1905. | |
| 1906. | |
| 1907. | |
| 1908. | |
| 1909. | |
| 1910. | |
| 1911. | |
| 1912. | |
| 1913. | |
| 1914. | |
| 1915. | |
| 1916. | |
| 1917. | |
| 1918. | |
| 1919. | |
| 1920. | |
| 1921. | |
| 1922. | |
| 1923. | |
| 1924. | |
| 1925. | |
| 1926. | |
| 1927. | |
| 1928. | |
| 1929. | |
| 1930. | |
| 1931. | |
| 1932. | |
| 1933. | |
| 1934. | |
| 1935. | |
| 1936. | |
| 1937. | |
| 1938. | |
| 1939. | |
| 1940. | |
| 1941. | |
| 1942. | |
| 1943. | |
| 1944. | |
| 1945. | |
| 1946. | |
| 1947. | |
| 1948. | |
| 1949. | |
| 1950. | |
| 1951. | |
| 1952. | |
| 1953. | |
| 1954. | |
| 1955. | |
| 1956. | |
| 1957. | |
| 1958. | |
| 1959. | |
| 1960. | |
| 1961. | |
| 1962. | |
| 1963. | |
| 1964. | |
| 1965. | |
| 1966. | |
| 1967. | |
| 1968. | |
| 1969. | |
| 1970. | |
| 1971. | |
| 1972. | |
| 1973. | |
| 1974. | |
| 1975. | |
| 1976. | |
| 1977. | |
| 1978. | |
| 1979. | |
| 1980. | |
| 1981. | |
| 1982. | |
| 1983. | |
| 1984. | |
| 1985. | |
| 1986. | |
| 1987. | |
| 1988. | |
| 1989. | |
| 1990. | |
| 1991. | |
| 1992. | |
| 1993. | |
| 1994. | |
| 1995. | |
| 1996. | |
| 1997. | |
| 1998. | |
| 1999. | |
| 2000. | |
| 2001. | |
| 2002. | |
| 2003. | |
| 2004. | |
| 2005. | |
| 2006. | |
| 2007. | |
| 2008. | |
| 2009. | |
| 2010. | |
| 2011. | |
| 2012. | |
| 2013. | |
| 2014. | |
| 2015. | |
| 2016. | |
| 2017. | |
| 2018. | |
| 2019. | |
| 2020. | |
| 2021. | |
| 2022. | |
| 2023. | |
| 2024. | |
| 2025. | |
| 2026. | |
| 2027. | |
| 2028. | |
| 2029. | |
| 2030. | |
| 2031. | |
| 2032. | |
| 2033. | |
| 2034. | |
| 2035. | |
| 2036. | |
| 2037. | |
| 2038. | |
| 2039. | |
| 2040. | |
| 2041. | |
| 2042. | |
| 2043. | |
| 2044. | |
| 2045. | |
| 2046. | |
| 2047. | |
| 2048. | |
| 2049. | |
| 2050. | |
| 2051. | |
| 2052. | |
| 2053. | |
| 2054. | |
| 2055. | |
| 2056. | |
| 2057. | |
| 2058. | |
| 2059. | |
| 2060. | |
| 2061. | |
| 2062. | |
| 2063. | |
| 2064. | |
| 2065. | |
| 2066. | |
| 2067. | |
| 2068. | |
| 2069. | |
| 2070. | |
| 2071. | |
| 2072. | |
| 2073. | |
| 2074. | |
| 2075. | |
| 2076. | |
| 2077. | |
| 2078. | |
| 2079. | |
| 2080. | |
| 2081. | |
| 2082. | |
| 2083. | |
| 2084. | |
| 2085. | |
| 2086. | |
| 2087. | |
| 2088. | |
| 2089. | |
| 2090. | |
| 2091. | |
| 2092. | |
| 2093. | |
| 2094. | |
| 2095. | |
| 2096. | |
| 2097. | |
| 2098. | |
| 2099. | |
| 2100. | |
| 2101. | |
| 2102. | |
| 2103. | |
| 2104. | |
| 2105. | |
| 2106. | |
| 2107. | |
| 2108. | |
| 2109. | |
| 2110. | |
| 2111. | |
| 2112. | |
| 2113. | |
| 2114. | |
| 2115. | |
| 2116. | |
| 2117. | |
| 2118. | |
| 2119. | |
| 2120. | |
| 2121. | |
| 2122. | |
| 2123. | |
| 2124. | |
| 2125. | |
| 2126. | |
| 2127. | |
| 2128. | |
| 2129. | |
| 2130. | |
| 2131. | |
| 2132. | |
| 2133. | |
| 2134. | |
| 2135. | |
| 2136. | |
| 2137. | |
| 2138. | |
| 2139. | |
| 2140. | |
| 2141. | |
| 2142. | |
| 2143. | |
| 2144. | |
| 2145. | |
| 2146. | |
| 2147. | |
| 2148. | |
| 2149. | |
| 2150. | |
| 2151. | |
| 2152. | |
| 2153. | |
| 2154. | |
| 2155. | |
| 2156. | |
| 2157. | |
| 2158. | |
| 2159. | |
| 2160. | |
| 2161. | |
| 2162. | |
| 2163. | |
| 2164. | |
| 2165. | |
| 2166. | |
| 2167. | |
| 2168. | |
| 2169. | |
| 2170. | |
| 2171. | |
| 2172. | |
| 2173. | |
| 2174. | |
| 2175. | |
| 2176. | |
| 2177. | |
| 2178. | |
| 2179. | |
| 2180. | |
| 2181. | |
| 2182. | |
| 2183. | |
| 2184. | |
| 2185. | |
| 2186. | |
| 2187. | |
| 2188. | |
| 2189. | |
| 2190. | |
| 2191. | |
| 2192. | |
| 2193. | |
| 2194. | |
| 2195. | |
| 2196. | |
| 2197. | |
| 2198. | |
| 2199. | |
| 2200. | |
| 2201. | |
| 2202. | |
| 2203. | |
| 2204. | |
| 2205. | |
| 2206. | |
| 2207. | |
| 2208. | |
| 2209. | |
| 2210. | |
| 2211. | |
| 2212. | |
| 2213. | |
| 2214. | |
| 2215. | |
| 2216. | |
| 2217. | |
| 2218. | |
| 2219. | |
| 2220. | |
| 2221. | |
| 2222. | |
| 2223. | |
| 2224. | |
| 2225. | |
| 2226. | |
| 2227. | |
| 2228. | |
| 2229. | |
| 2230. | |
| 2231. | |
| 2232. | |
| 2233. | |
| 2234. | |
| 2235. | |
| 2236. | |
| 2237. | |
| 2238. | |
| 2239. | |
| 2240. | |
| 2241. | |
| 2242. | |
| 2243. | |
| 2244. | |
| 2245. | |
| 2246. | |
| 2247. | |
| 2248. | |
| 2249. | |
| 2250. | |
| 2251. | |
| 2252. | |
| 2253. | |
| 2254. | |
| 2255. | |
| 2256. | |
| 2257. | |
| 2258. | |
| 2259. | |
| 2260. | |
| 2261. | |
| 2262. | |
| 2263. | |
| 2264. | |
| 2265. | |
| 2266. | |
| 2267. | |
| 2268. | |
| 2269. | |
| 2270. | |
| 2271. | |
| 2272. | |
| 2273. | |
| 2274. | |
| 2275. | |
| 2276. | |
| 2277. | |
| 2278. | |
| 2279. | |
| 2280. | |
| 2281. | |
| 2282. | |
| 2283. | |
| 2284. | |
| 2285. | |
| 2286. | |
| 2287. | |
| 2288. | |
| 2289. | |
| 2290. | |
| 2291. | |
| 2292. | |
| 2293. | |
| 2294. | |
| 2295. | |
| 2296. | |
| 2297. | |
| 2298. | |
| 2299. | |
| 2300. | |
| 2301. | |
| 2302. | |
| 2303. | |
| 2304. | |
| 2305. | |
| 2306. | |
| 2307. | |
| 2308. | |
| 2309. | |
| 2310. | |
| 2311. | |
| 2312. | |
| 2313. | |
| 2314. | |
| 2315. | |
| 2316. | |
| 2317. | |
| 2318. | |
| 2319. | |
| 2320. | |
| 2321. | |
| 2322. | |
| 2323. | |
| 2324. | |
| 2325. | |
| 2326. | |
| 2327. | |
| 2328. | |
| 2329. | |
| 2330. | |
| 2331. | |
| 2332. | |
| 2333. | |
| 2334. | |
| 2335. | |
| 2336. | |
| 2337. | |
| 2338. | |
| 2339. | |
| 2340. | |
| 2341. | |
| 2342. | |
| 2343. | |
| 2344. | |
| 2345. | |
| 2346. | |
| 2347. | |
| 2348. | |
| 2349. | |
| 2350. | |
| 2351. | |
| 2352. | |
| 2353. | |
| 2354. | |
| 2355. | |
| 2356. | |
| 2357. | |
| 2358. | |
| 2359. | |
| 2360. | |
| 2361. | |
| 2362. | |
| 2363. | |
| 2364. | |
| 2365. | |
| 2366. | |
| 2367. | |
| 2368. | |
| 2369. | |
| 2370. | |
| 2371. | |
| 2372. | |
| 2373. | |
| 2374. | |
| 2375. | |
| 2376. | |
| 2377. | |
| 2378. | |
| 2379. | |
| 2380. | |
| 2381. | |
| 2382. | |
| 2383. | |
| 2384. | |
| 2385. | |
| 2386. | |
| 2387. | |
| 2388. | |
| 2389. | |
| 2390. | |
| 2391. | |
| 2392. | |
| 2393. | |
| 2394. | |
| 2395. | |
| 2396. | |
| 2397. | |
| 2398. | |
| 2399. | |
| 2400. | |
| 2401. | |
| 2402. | |
| 2403. | |
| 2404. | |
| 2405. | |
| 2406. | |
| 2407. | |
| 2408. | |
| 2409. | |
| 2410. | |
| 2411. | |
| 2412. | |
| 2413. | |
| 2414. | |
| 2415. | |
| 2416. | |
| 2417. | |
| 2418. | |
| 2419. | |
| 2420. | |
| # -*- coding: utf-8 -*- | |
| """ | |
| SahanaPy RESTlike CRUD Controller | |
| @author: Fran Boon | |
| @author: nursix | |
| @see: U{http://trac.sahanapy.org/wiki/RESTController} | |
| HTTP Status Codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html | |
| """ | |
| # ***************************************************************************** | |
| # Constants to ensure consistency | |
| # XSLT Settings | |
| XSLT_FILE_EXTENSION = 'xsl' #: File extension of XSLT templates | |
| XSLT_IMPORT_TEMPLATES = 'static/xslt/import' #: Path to XSLT templates for data import | |
| XSLT_EXPORT_TEMPLATES = 'static/xslt/export' #: Path to XSLT templates for data export | |
| # XSLT available formats | |
| shn_xml_import_formats = ["xml", "lmx"] #: Supported XML import formats | |
| shn_xml_export_formats = dict( | |
| xml = "application/xml", | |
| lmx = "application/xml", | |
| georss = "application/rss+xml", | |
| kml = "application/vnd.google-earth.kml+xml" | |
| ) #: Supported XML output formats and corresponding response headers | |
| shn_json_import_formats = ["json"] #: Supported JSON import formats | |
| shn_json_export_formats = dict( | |
| json = "text/x-json" | |
| ) #: Supported JSON output formats and corresponding response headers | |
| # Error messages | |
| UNAUTHORISED = T('Not authorised!') | |
| BADFORMAT = T('Unsupported data format!') | |
| BADMETHOD = T('Unsupported method!') | |
| BADRECORD = T('No such record!') | |
| INVALIDREQUEST = T('Invalid request!') | |
| # How many rows to show per page in list outputs | |
| ROWSPERPAGE = 20 | |
| PRETTY_PRINT = True | |
| # ***************************************************************************** | |
| # S3XRC ResourceController | |
| exec('from applications.%s.modules.s3xrc import ResourceController' % request.application) | |
| exec('from applications.%s.modules.s3xrc import json_message' % request.application) | |
| # Faster for Production (where app-name won't change): | |
| #from applications.sahana.modules.s3xrc import ResourceController | |
| #from applications.sahana.modules.s3xrc import json_message | |
| s3xrc = ResourceController(db, | |
| domain=request.env.server_name, | |
| base_url="%s/%s" % (S3_PUBLIC_URL, request.application), | |
| rpp=ROWSPERPAGE) | |
| # ***************************************************************************** | |
| # Exports | |
| # | |
| # export_csv ------------------------------------------------------------------ | |
| # | |
| def export_csv(resource, query, record=None): | |
| """ Export record(s) as CSV """ | |
| import gluon.contenttype | |
| response.headers['Content-Type'] = gluon.contenttype.contenttype('.csv') | |
| if record: | |
| filename = "%s_%s_%d.csv" % (request.env.server_name, resource, record) | |
| else: | |
| # List | |
| filename = "%s_%s_list.csv" % (request.env.server_name, resource) | |
| response.headers['Content-disposition'] = "attachment; filename=%s" % filename | |
| return str(db(query).select()) | |
| # | |
| # export_pdf ------------------------------------------------------------------ | |
| # | |
| def export_pdf(table, query): | |
| """ Export record(s) as Adobe PDF """ | |
| try: | |
| from reportlab.lib.units import cm | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.enums import TA_CENTER, TA_RIGHT | |
| except ImportError: | |
| session.error = T('ReportLab module not available within the running Python - this needs installing for PDF output!') | |
| redirect(URL(r=request)) | |
| try: | |
| from geraldo import Report, ReportBand, Label, ObjectValue, SystemField, landscape, BAND_WIDTH | |
| from geraldo.generators import PDFGenerator | |
| except ImportError: | |
| session.error = T('Geraldo module not available within the running Python - this needs installing for PDF output!') | |
| redirect(URL(r=request)) | |
| objects_list = db(query).select(table.ALL) | |
| if not objects_list: | |
| session.warning = T('No data in this table - cannot create PDF!') | |
| redirect(URL(r=request)) | |
| import StringIO | |
| output = StringIO.StringIO() | |
| fields = [table[f] for f in table.fields if table[f].readable] | |
| _elements = [SystemField(expression='%(report_title)s', top=0.1*cm, | |
| left=0, width=BAND_WIDTH, style={'fontName': 'Helvetica-Bold', | |
| 'fontSize': 14, 'alignment': TA_CENTER})] | |
| detailElements = [] | |
| COLWIDTH = 2.5 | |
| LEFTMARGIN = 0.2 | |
| for field in fields: | |
| _elements.append(Label(text=str(field.label)[:16], top=0.8*cm, left=LEFTMARGIN*cm)) | |
| tab, col = str(field).split('.') | |
| #db[table][col].represent = 'foo' | |
| detailElements.append(ObjectValue( | |
| attribute_name=col, | |
| left=LEFTMARGIN*cm, width=COLWIDTH*cm, | |
| get_value=lambda instance: str(instance).decode('utf-8'))) | |
| LEFTMARGIN += COLWIDTH | |
| mod, res = str(table).split('_', 1) | |
| mod_nice = db(db.s3_module.name==mod).select()[0].name_nice | |
| _title = mod_nice + ': ' + res.capitalize() | |
| class MyReport(Report): | |
| title = _title | |
| page_size = landscape(A4) | |
| class band_page_header(ReportBand): | |
| height = 1.3*cm | |
| auto_expand_height = True | |
| elements = _elements | |
| borders = {'bottom': True} | |
| class band_page_footer(ReportBand): | |
| height = 0.5*cm | |
| elements = [ | |
| Label(text='%s' % request.utcnow.date(), top=0.1*cm, left=0), | |
| SystemField(expression='Page # %(page_number)d of %(page_count)d', top=0.1*cm, | |
| width=BAND_WIDTH, style={'alignment': TA_RIGHT}), | |
| ] | |
| borders = {'top': True} | |
| class band_detail(ReportBand): | |
| height = 0.5*cm | |
| auto_expand_height = True | |
| elements = tuple(detailElements) | |
| report = MyReport(queryset=objects_list) | |
| report.generate_by(PDFGenerator, filename=output) | |
| output.seek(0) | |
| import gluon.contenttype | |
| response.headers['Content-Type'] = gluon.contenttype.contenttype('.pdf') | |
| filename = "%s_%s.pdf" % (request.env.server_name, str(table)) | |
| response.headers['Content-disposition'] = "attachment; filename=\"%s\"" % filename | |
| return output.read() | |
| # | |
| # export_rss ------------------------------------------------------------------ | |
| # | |
| def export_rss(module, resource, query, rss=None, linkto=None): | |
| """ Export record(s) as RSS feed """ | |
| # This can not work when proxied through Apache (since it's always a local request): | |
| #if request.env.remote_addr == '127.0.0.1': | |
| #server = 'http://127.0.0.1:' + request.env.server_port | |
| #else: | |
| #server = 'http://' + request.env.server_name + ':' + request.env.server_port | |
| server = S3_PUBLIC_URL | |
| tablename = "%s_%s" % (module, resource) | |
| try: | |
| title_list = s3.crud_strings[tablename].subtitle_list | |
| except: | |
| title_list = s3.crud_strings.subtitle_list | |
| if not linkto: | |
| link = '/%s/%s/%s' % (request.application, module, resource) | |
| else: | |
| link = linkto | |
| entries = [] | |
| rows = db(query).select() | |
| if rows: | |
| for row in rows: | |
| if rss and 'title' in rss: | |
| try: | |
| title = rss.get('title')(row) | |
| except TypeError: | |
| title = rss.get('title') % row | |
| else: | |
| title = row['id'] | |
| if rss and 'description' in rss: | |
| try: | |
| description = rss.get('description')(row) | |
| except TypeError: | |
| description = rss.get('description') % row | |
| else: | |
| description = '' | |
| entries.append(dict( | |
| title=str(title).decode('utf-8'), | |
| link=server + link + '/%d' % row.id, | |
| description=str(description).decode('utf-8'), | |
| created_on=row.created_on)) | |
| import gluon.contrib.rss2 as rss2 | |
| items = [rss2.RSSItem( | |
| title = entry['title'], | |
| link = entry['link'], | |
| description = entry['description'], | |
| pubDate = entry['created_on']) for entry in entries] | |
| rss = rss2.RSS2( | |
| title = str(title_list).decode('utf-8'), | |
| link = server + link, | |
| description = '', | |
| lastBuildDate = request.utcnow, | |
| items = items) | |
| response.headers['Content-Type'] = 'application/rss+xml' | |
| return rss2.dumps(rss) | |
| # | |
| # export_xls ------------------------------------------------------------------ | |
| # | |
| def export_xls(table, query): | |
| """ Export record(s) as XLS """ | |
| try: | |
| import xlwt | |
| except ImportError: | |
| session.error = T('xlwt module not available within the running Python - this needs installing for XLS output!') | |
| redirect(URL(r=request)) | |
| import StringIO | |
| output = StringIO.StringIO() | |
| items = db(query).select(table.ALL) | |
| book = xlwt.Workbook(encoding='utf-8') | |
| sheet1 = book.add_sheet(str(table)) | |
| # Header row | |
| row0 = sheet1.row(0) | |
| cell = 0 | |
| fields = [table[f] for f in table.fields if table[f].readable] | |
| for field in fields: | |
| row0.write(cell, str(field.label), xlwt.easyxf('font: bold True;')) | |
| cell += 1 | |
| row = 1 | |
| for item in items: | |
| # Item details | |
| rowx = sheet1.row(row) | |
| row += 1 | |
| cell1 = 0 | |
| for field in fields: | |
| style = xlwt.XFStyle() | |
| tab, col = str(field).split('.') | |
| # Check for Date formats | |
| if db[tab][col].type == 'date': | |
| style.num_format_str = 'D-MMM-YY' | |
| elif db[tab][col].type == 'datetime': | |
| style.num_format_str = 'M/D/YY h:mm' | |
| elif db[tab][col].type == 'time': | |
| style.num_format_str = 'h:mm:ss' | |
| # Check for a custom.represent (e.g. for ref fields) | |
| try: | |
| represent = str(field.represent(item[col])) | |
| except: | |
| represent = item[col] | |
| rowx.write(cell1, str(represent), style) | |
| cell1 += 1 | |
| book.save(output) | |
| output.seek(0) | |
| import gluon.contenttype | |
| response.headers['Content-Type'] = gluon.contenttype.contenttype('.xls') | |
| filename = "%s_%s.xls" % (request.env.server_name, str(table)) | |
| response.headers['Content-disposition'] = "attachment; filename=\"%s\"" % filename | |
| return output.read() | |
| # | |
| # export_json ----------------------------------------------------------------- | |
| # | |
| def export_json(jr): | |
| """ Export data as JSON """ | |
| try: | |
| response.headers['Content-Type'] = shn_json_export_formats[jr.representation] | |
| except: | |
| response.headers['Content-Type'] = "text/x-json" | |
| if jr.representation=="json": | |
| template = None | |
| else: | |
| template_name = "%s.%s" % (jr.representation, XSLT_FILE_EXTENSION) | |
| template = os.path.join(request.folder, XSLT_EXPORT_TEMPLATES, template_name) | |
| if not os.path.exists(template): | |
| session.error = str(T("XSLT Template Not Found: ")) + \ | |
| XSLT_EXPORT_TEMPLATES + "/" + template_name | |
| raise HTTP(501, body=json_message(False, 501, session.error)) | |
| #redirect(URL(r=request, f="index")) | |
| output = jr.export_json(permit=shn_has_permission, | |
| audit=shn_audit, | |
| template=template, | |
| pretty_print=PRETTY_PRINT, | |
| filterby=response.s3.filter) | |
| if not output: | |
| session.error = str(T("XSLT Transformation Error: ")) + jr.error | |
| raise HTTP(400, body=json_message(False, 400, session.error)) | |
| #redirect(URL(r=request, f="index")) | |
| return output | |
| # | |
| # export_xml ------------------------------------------------------------------ | |
| # | |
| def export_xml(jr): | |
| """ Export data as XML """ | |
| try: | |
| response.headers['Content-Type'] = shn_xml_export_formats[jr.representation] | |
| except: | |
| response.headers['Content-Type'] = "application/xml" | |
| if jr.representation=="xml": | |
| template = None | |
| else: | |
| template_name = "%s.%s" % (jr.representation, XSLT_FILE_EXTENSION) | |
| template = os.path.join(request.folder, XSLT_EXPORT_TEMPLATES, template_name) | |
| if not os.path.exists(template): | |
| session.error = str(T("XSLT Template Not Found: ")) + \ | |
| XSLT_EXPORT_TEMPLATES + "/" + template_name | |
| raise HTTP(501, body=json_message(False, 501, session.error)) | |
| #redirect(URL(r=request, f="index")) | |
| output = jr.export_xml(permit=shn_has_permission, | |
| audit=shn_audit, | |
| template=template, | |
| pretty_print=PRETTY_PRINT, | |
| filterby=response.s3.filter) | |
| if not output: | |
| session.error = str(T("XSLT Transformation Error: ")) + jr.error | |
| raise HTTP(400, body=json_message(False, 400, session.error)) | |
| #redirect(URL(r=request, f="index")) | |
| return output | |
| # ***************************************************************************** | |
| # Imports | |
| # | |
| # import_csv ------------------------------------------------------------------ | |
| # | |
| def import_csv(file, table=None): | |
| """ Import CSV file into Database """ | |
| if table: | |
| table.import_from_csv_file(file) | |
| else: | |
| # This is the preferred method as it updates reference fields | |
| db.import_from_csv_file(file) | |
| # | |
| # import_url ------------------------------------------------------------------ | |
| # | |
| def import_url(jr, table, method, onvalidation=None, onaccept=None): | |
| """ | |
| Import GET/URL vars into Database & respond in JSON, | |
| supported methods: 'create' & 'update' | |
| """ | |
| record = Storage() | |
| uuid = None | |
| original = None | |
| module, resource, table, tablename = jr.target() | |
| if jr.component: | |
| onvalidation = s3xrc.model.get_attr(resource, "onvalidation") | |
| onaccept = s3xrc.model.get_attr(resource, "onaccept") | |
| response.headers['Content-Type'] = 'text/x-json' | |
| for var in request.vars: | |
| # Skip the Representation | |
| if var == 'format': | |
| continue | |
| elif var == 'uuid': | |
| uuid = request.vars[var] | |
| elif table[var].type == 'upload': | |
| # Handle file uploads (copied from gluon/sqlhtml.py) | |
| field = table[var] | |
| fieldname = var | |
| f = request.vars[fieldname] | |
| fd = fieldname + '__delete' | |
| if f == '' or f == None: | |
| #if request.vars.get(fd, False) or not self.record: | |
| if request.vars.get(fd, False): | |
| record[fieldname] = '' | |
| else: | |
| #record[fieldname] = self.record[fieldname] | |
| pass | |
| elif hasattr(f,'file'): | |
| (source_file, original_filename) = (f.file, f.filename) | |
| elif isinstance(f, (str, unicode)): | |
| ### do not know why this happens, it should not | |
| (source_file, original_filename) = \ | |
| (cStringIO.StringIO(f), 'file.txt') | |
| newfilename = field.store(source_file, original_filename) | |
| request.vars['%s_newfilename' % fieldname] = record[fieldname] = newfilename | |
| if field.uploadfield and not field.uploadfield==True: | |
| record[field.uploadfield] = source_file.read() | |
| else: | |
| record[var] = request.vars[var] | |
| # UUID is required for update | |
| if method == 'update': | |
| if uuid: | |
| try: | |
| original = db(table.uuid==uuid).select(table.ALL)[0] | |
| except: | |
| raise HTTP(404, body=json_message(False, 404, "Record not found!")) | |
| else: | |
| # You will never come to that point without having specified a | |
| # record ID in the request. Nevertheless, we require a UUID to | |
| # identify the record | |
| raise HTTP(400, body=json_message(False, 400, "UUID required!")) | |
| # Validate record | |
| for var in record: | |
| if var in table.fields: | |
| value = record[var] | |
| (value, error) = s3xrc.xml.validate(table, original, var, value) | |
| else: | |
| # Shall we just ignore non-existent fields? | |
| # del record[var] | |
| error = "Invalid field name." | |
| if error: | |
| raise HTTP(400, body=json_message(False, 400, var + " invalid: " + error)) | |
| else: | |
| record[var] = value | |
| form = Storage() | |
| form.method = method | |
| form.vars = record | |
| # Onvalidation callback | |
| if onvalidation: | |
| onvalidation(form) | |
| # Create/update record | |
| try: | |
| if jr.component: | |
| record[jr.fkey]=jr.record[jr.pkey] | |
| if method == 'create': | |
| id = table.insert(**dict(record)) | |
| if id: | |
| error = 201 | |
| item = json_message(True, error, "Created as " + str(jr.other(method=None, record_id=id))) | |
| form.vars.id = id | |
| if onaccept: | |
| onaccept(form) | |
| else: | |
| error = 403 | |
| item = json_message(False, error, "Could not create record!") | |
| elif method == 'update': | |
| result = db(table.uuid==uuid).update(**dict(record)) | |
| if result: | |
| error = 200 | |
| item = json_message(True, error, "Record updated.") | |
| form.vars.id = original.id | |
| if onaccept: | |
| onaccept(form) | |
| else: | |
| error = 403 | |
| item = json_message(False, error, "Could not update record!") | |
| else: | |
| error = 501 | |
| item = json_message(False, error, "Unsupported Method!") | |
| except: | |
| error = 400 | |
| item = json_message(False, error, "Invalid request!") | |
| raise HTTP(error, body=item) | |
| # | |
| # import_json ----------------------------------------------------------------- | |
| # | |
| def import_json(jr, onvalidation=None, onaccept=None): | |
| #return json_message(False, 501, "Not implemented!") | |
| if "filename" in jr.request.vars: | |
| source = open(jr.request.vars["filename"]) | |
| elif "fetchurl" in jr.request.vars: | |
| import urllib | |
| source = urllib.urlopen(jr.request.vars["fetchurl"]) | |
| else: | |
| #from StringIO import StringIO | |
| #source = StringIO(jr.request.body) | |
| source = jr.request.body | |
| tree = s3xrc.xml.json2tree(source) | |
| if hasattr(source, "close"): | |
| source.close() | |
| # XSLT Transformation | |
| if not jr.representation=="json": | |
| template_name = "%s.%s" % (jr.representation, XSLT_FILE_EXTENSION) | |
| template_file = os.path.join(request.folder, XSLT_IMPORT_TEMPLATES, template_name) | |
| if os.path.exists(template_file): | |
| tree = s3xrc.xml.transform(tree, template_file) | |
| if not tree: | |
| session.error = str(T("XSL Transformation Error: ")) + str(s3xrc.xml.error) | |
| redirect(URL(r=request, f="index")) | |
| else: | |
| session.error = str(T("XSL Template Not Found: ")) + \ | |
| XSLT_IMPORT_TEMPLATES + "/" + template_name | |
| #redirect(URL(r=request, f="index")) | |
| item = json_message(False, 501, session.error) | |
| raise HTTP(501) | |
| # For testing: | |
| #print s3xrc.xml.tostring(tree) | |
| #return s3xrc.xml.tree2json(tree) | |
| success = jr.import_xml(tree, | |
| permit=shn_has_permission, | |
| audit=shn_audit, | |
| onvalidation=onvalidation, | |
| onaccept=onaccept) | |
| if success: | |
| item = json_message() | |
| else: | |
| # TODO: export the whole tree on error | |
| tree = s3xrc.xml.tree2json(tree) | |
| item = json_message(False, 400, s3xrc.xml.error, tree=tree) | |
| raise HTTP(400, body=item) | |
| return dict(item=item) | |
| # | |
| # import_xml ------------------------------------------------------------------ | |
| # | |
| def import_xml(jr, onvalidation=None, onaccept=None): | |
| """ Import XML data """ | |
| if "filename" in jr.request.vars: | |
| source = jr.request.vars["filename"] | |
| elif "fetchurl" in jr.request.vars: | |
| source = jr.request.vars["fetchurl"] | |
| else: | |
| source = jr.request.body | |
| tree = s3xrc.xml.parse(source) | |
| # XSLT Transformation | |
| if not jr.representation=="xml": | |
| template_name = "%s.%s" % (jr.representation, XSLT_FILE_EXTENSION) | |
| template_file = os.path.join(request.folder, XSLT_IMPORT_TEMPLATES, template_name) | |
| if os.path.exists(template_file): | |
| tree = s3xrc.xml.transform(tree, template_file) | |
| if not tree: | |
| session.error = str(T("XSLT Transformation Error: ")) + str(s3xrc.xml.error) | |
| redirect(URL(r=request, f="index")) | |
| else: | |
| session.error = str(T("XSLT Template Not Found: ")) + \ | |
| XSLT_IMPORT_TEMPLATES + "/" + template_name | |
| #redirect(URL(r=request, f="index")) | |
| item = json_message(False, 501, session.error) | |
| raise HTTP(501) | |
| success = jr.import_xml(tree, | |
| permit=shn_has_permission, | |
| audit=shn_audit, | |
| onvalidation=onvalidation, | |
| onaccept=onaccept) | |
| if success: | |
| item = json_message() | |
| else: | |
| # TODO: export the whole tree on error | |
| tree = s3xrc.xml.tree2json(tree) | |
| item = json_message(False, 400, s3xrc.xml.error, tree=tree) | |
| raise HTTP(400, body=item) | |
| return dict(item=item) | |
| # ***************************************************************************** | |
| # Authorisation | |
| # | |
| # shn_has_permission ---------------------------------------------------------- | |
| # | |
| def shn_has_permission(name, table_name, record_id = 0): | |
| """ | |
| S3 framework function to define whether a user can access a record | |
| Designed to be called from the RESTlike controller | |
| """ | |
| if session.s3.security_policy == 1: | |
| # Simple policy | |
| # Anonymous users can Read. | |
| if name == 'read': | |
| authorised = True | |
| else: | |
| # Authentication required for Create/Update/Delete. | |
| authorised = auth.is_logged_in() or auth.basic() | |
| else: | |
| # Full policy | |
| if auth.is_logged_in() or auth.basic(): | |
| # Administrators are always authorised | |
| if auth.has_membership(1): | |
| authorised = True | |
| else: | |
| # Require records in auth_permission to specify access | |
| authorised = auth.has_permission(name, table_name, record_id) | |
| else: | |
| # No access for anonymous | |
| authorised = False | |
| return authorised | |
| # | |
| # shn_accessible_query -------------------------------------------------------- | |
| # | |
| def shn_accessible_query(name, table): | |
| """ | |
| Returns a query with all accessible records for the current logged in user | |
| @note: This method does not work on GAE because uses JOIN and IN | |
| """ | |
| # If using the 'simple' security policy then show all records | |
| if session.s3.security_policy == 1: | |
| # simple | |
| return table.id > 0 | |
| # Administrators can see all data | |
| if auth.has_membership(1): | |
| return table.id > 0 | |
| # If there is access to the entire table then show all records | |
| try: | |
| user_id = auth.user.id | |
| except: | |
| user_id = 0 | |
| if auth.has_permission(name, table, 0, user_id): | |
| return table.id > 0 | |
| # Filter Records to show only those to which the user has access | |
| session.warning = T("Only showing accessible records!") | |
| membership = auth.settings.table_membership | |
| permission = auth.settings.table_permission | |
| return table.id.belongs(db(membership.user_id == user_id)\ | |
| (membership.group_id == permission.group_id)\ | |
| (permission.name == name)\ | |
| (permission.table_name == table)\ | |
| ._select(permission.record_id)) | |
| # ***************************************************************************** | |
| # Audit | |
| # | |
| # These functions should always return True in order to be chainable | |
| # by 'and' for lambda's as onaccept-callbacks. -- nursix -- | |
| # | |
| # shn_audit ------------------------------------------------------------------- | |
| # | |
| def shn_audit(operation, module, resource, form=None, record=None, representation=None): | |
| #print "Audit: %s on %s_%s #%s" % (operation, module, resource, record or 0) | |
| if operation in ("list", "read"): | |
| return shn_audit_read(operation, module, resource, | |
| record=record, representation=representation) | |
| elif operation=="create": | |
| return shn_audit_create(form, module, resource, representation=representation) | |
| elif operation=="update": | |
| return shn_audit_update(form, module, resource, representation=representation) | |
| elif operation=="delete": | |
| return shn_audit_create(module, resource, record, representation=representation) | |
| return True | |
| # | |
| # shn_audit_read -------------------------------------------------------------- | |
| # | |
| def shn_audit_read(operation, module, resource, record=None, representation=None): | |
| """ Called during Read operations to enable optional Auditing """ | |
| if session.s3.audit_read: | |
| db.s3_audit.insert( | |
| person = auth.user.id if session.auth else 0, | |
| operation = operation, | |
| module = module, | |
| resource = resource, | |
| record = record, | |
| representation = representation, | |
| ) | |
| return True | |
| # | |
| # shn_audit_create ------------------------------------------------------------ | |
| # | |
| def shn_audit_create(form, module, resource, representation=None): | |
| """ | |
| Called during Create operations to enable optional Auditing | |
| Called as an onaccept so that it only takes effect when | |
| saved & can read the new values in:: | |
| onaccept = lambda form: shn_audit_create(form, module, resource, representation) | |
| """ | |
| if session.s3.audit_write: | |
| record = form.vars.id | |
| new_value = [] | |
| for var in form.vars: | |
| new_value.append(var + ':' + str(form.vars[var])) | |
| db.s3_audit.insert( | |
| person = auth.user.id if session.auth else 0, | |
| operation = 'create', | |
| module = module, | |
| resource = resource, | |
| record = record, | |
| representation = representation, | |
| new_value = new_value | |
| ) | |
| return True | |
| # | |
| # shn_audit_update ------------------------------------------------------------ | |
| # | |
| def shn_audit_update(form, module, resource, representation=None): | |
| """ | |
| Called during Create operations to enable optional Auditing | |
| Called as an onaccept so that it only takes effect when | |
| saved & can read the new values in:: | |
| onaccept = lambda form: shn_audit_update(form, module, resource, representation) | |
| """ | |
| if session.s3.audit_write: | |
| record = form.vars.id | |
| new_value = [] | |
| for var in form.vars: | |
| new_value.append(var + ':' + str(form.vars[var])) | |
| db.s3_audit.insert( | |
| person = auth.user.id if session.auth else 0, | |
| operation = 'update', | |
| module = module, | |
| resource = resource, | |
| record = record, | |
| representation = representation, | |
| #old_value = old_value, # Need to store these beforehand if we want them | |
| new_value = new_value | |
| ) | |
| return True | |
| # | |
| # shn_audit_update_m2m -------------------------------------------------------- | |
| # | |
| def shn_audit_update_m2m(module, resource, record, representation=None): | |
| """ | |
| Called during Update operations to enable optional Auditing | |
| Designed for use in M2M 'Update Qty/Delete' (which can't use crud.settings.update_onaccept) | |
| shn_audit_update_m2m(resource, record, representation) | |
| """ | |
| if session.s3.audit_write: | |
| db.s3_audit.insert( | |
| person = auth.user.id if session.auth else 0, | |
| operation = 'update', | |
| module = module, | |
| resource = resource, | |
| record = record, | |
| representation = representation, | |
| #old_value = old_value, # Need to store these beforehand if we want them | |
| #new_value = new_value # Various changes can happen, so would need to store dict of {item_id: qty} | |
| ) | |
| return True | |
| # | |
| # shn_audit_delete ------------------------------------------------------------ | |
| # | |
| def shn_audit_delete(module, resource, record, representation=None): | |
| """ Called during Delete operations to enable optional Auditing """ | |
| if session.s3.audit_write: | |
| module = module | |
| table = '%s_%s' % (module, resource) | |
| old_value = [] | |
| _old_value = db(db[table].id==record).select()[0] | |
| for field in _old_value: | |
| old_value.append(field + ':' + str(_old_value[field])) | |
| db.s3_audit.insert( | |
| person = auth.user.id if session.auth else 0, | |
| operation = 'delete', | |
| module = module, | |
| resource = resource, | |
| record = record, | |
| representation = representation, | |
| old_value = old_value | |
| ) | |
| return True | |
| # ***************************************************************************** | |
| # Display Representations | |
| # t2.itemize now deprecated | |
| # but still used for t2.search | |
| # | |
| # shn_represent --------------------------------------------------------------- | |
| # | |
| def shn_represent(table, module, resource, deletable=True, main='name', extra=None): | |
| """ Designed to be called via table.represent to make t2.search() output useful """ | |
| db[table].represent = lambda table:shn_list_item(table, resource, action='display', main=main, extra=shn_represent_extra(table, module, resource, deletable, extra)) | |
| return | |
| # | |
| # shn_represent_extra --------------------------------------------------------- | |
| # | |
| def shn_represent_extra(table, module, resource, deletable=True, extra=None): | |
| """ Display more than one extra field (separated by spaces)""" | |
| authorised = shn_has_permission('delete', table) | |
| item_list = [] | |
| if extra: | |
| extra_list = extra.split() | |
| for any_item in extra_list: | |
| item_list.append("TD(db(db.%s_%s.id==%i).select()[0].%s)" % \ | |
| (module, resource, table.id, any_item)) | |
| if authorised and deletable: | |
| item_list.append("TD(INPUT(_type='checkbox', _class='delete_row', _name='%s', _id='%i'))" % \ | |
| (resource, table.id)) | |
| return ','.join( item_list ) | |
| # | |
| # shn_list_item --------------------------------------------------------------- | |
| # | |
| def shn_list_item(table, resource, action, main='name', extra=None): | |
| """ Display nice names with clickable links & optional extra info """ | |
| item_list = [TD(A(table[main], _href=URL(r=request, f=resource, args=[action, table.id])))] | |
| if extra: | |
| item_list.extend(eval(extra)) | |
| items = DIV(TABLE(TR(item_list))) | |
| return DIV(*items) | |
| # | |
| # pagenav --------------------------------------------------------------------- | |
| # | |
| def pagenav(page=1, totalpages=None, first='1', prev='<', next='>', last='last', pagenums=10): | |
| """ Generate a page navigator around current record number """ | |
| # e.g. 1 < 3 4 5 > 36, | |
| # derived from: http://99babysteps.appspot.com/how2/default/article_read/2 | |
| if not totalpages: | |
| maxpages = page + 1 | |
| else: | |
| maxpages = totalpages | |
| page = min(page, totalpages) | |
| pagerange = pagenums / 2 # half the page numbers will be below the startpage, half above | |
| # create page selector list eg 1 2 3 | |
| pagelinks = [i for i in range(max(1, page - pagerange), min(page + pagerange, maxpages) + 1)] | |
| startpagepos = pagelinks.index(page) | |
| # make pagelist into hyperlinks: | |
| pagelinks = [A(str(pagelink), _href=URL(r=request, vars={'page':pagelink})) for pagelink in pagelinks] | |
| # remove link to current page & make text emphasised: | |
| pagelinks[startpagepos] = B(str(page)) | |
| if page < maxpages: | |
| nextlink = A(next, _href=URL(r=request, vars={'page':page + 1})) | |
| else: | |
| # no link if no next | |
| nextlink = next | |
| if page > 1: | |
| prevlink = A(prev, _href=URL(r=request, vars={'page':page - 1})) | |
| firstlink = A(first, _href=URL(r=request, vars={'page':1})) | |
| else: | |
| # no links if no prev | |
| prevlink = prev | |
| firstlink = DIV(first) | |
| if last <> '': | |
| if totalpages > 0: | |
| lasttext = last + '(' + str(totalpages) + ')' | |
| else: | |
| lasttext = last + '...' | |
| lastlink = A(lasttext, _href=URL(r=request, vars={'page':maxpages})) | |
| delim = XML(' ') # nonbreaking delim | |
| pagenav = firstlink | |
| pagenav.append(delim) | |
| pagenav.append(prevlink) | |
| pagenav.append(delim) | |
| for pageref in pagelinks: | |
| pagenav.append(pageref) | |
| pagenav.append(delim) | |
| pagenav.append(nextlink) | |
| pagenav.append(delim) | |
| pagenav.append(lastlink) | |
| return pagenav | |
| # | |
| # shn_custom_view ------------------------------------------------------------- | |
| # | |
| def shn_custom_view(jr, default_name, format=None): | |
| """ Check for custom view """ | |
| prefix = jr.request.controller | |
| if jr.component: | |
| custom_view = '%s_%s_%s' % (jr.name, jr.component_name, default_name) | |
| _custom_view = os.path.join(request.folder, 'views', prefix, custom_view) | |
| if not os.path.exists(_custom_view): | |
| custom_view = '%s_%s' % (jr.name, default_name) | |
| _custom_view = os.path.join(request.folder, 'views', prefix, custom_view) | |
| else: | |
| if format: | |
| custom_view = '%s_%s_%s' % (jr.name, default_name, format) | |
| else: | |
| custom_view = '%s_%s' % (jr.name, default_name) | |
| _custom_view = os.path.join(request.folder, 'views', prefix, custom_view) | |
| if os.path.exists(_custom_view): | |
| response.view = prefix + '/' + custom_view | |
| else: | |
| if format: | |
| response.view = default_name.replace('.html', '_%s.html' % format) | |
| else: | |
| response.view = default_name | |
| # ***************************************************************************** | |
| # CRUD Functions | |
| # | |
| # shn_read -------------------------------------------------------------------- | |
| # | |
| def shn_read(jr, pheader=None, editable=True, deletable=True, rss=None): | |
| """ Read a single record. """ | |
| module, resource, table, tablename = jr.target() | |
| if jr.component: | |
| query = (table[jr.fkey]==jr.record[jr.pkey]) | |
| if jr.component_id: | |
| query = (table.id==jr.component_id) & query | |
| if 'deleted' in table: | |
| query = ((table.deleted==False) | (table.deleted==None)) & query | |
| try: | |
| record_id = db(query).select(table.id)[0].id | |
| href_delete = URL(r=jr.request, f=jr.name, args=[jr.id, resource, 'delete', record_id]) | |
| href_edit = URL(r=jr.request, f=jr.name, args=[jr.id, resource, 'update', record_id]) | |
| except: | |
| if not jr.multiple: | |
| redirect(URL(r=jr.request, f=jr.name, args=[jr.id, resource, 'create'])) | |
| else: | |
| record_id = None | |
| href_delete = None | |
| href_edit = None | |
| session.error = BADRECORD | |
| redirect(jr.there()) # TODO: this is wrong when no records exist! | |
| editable = s3xrc.model.get_attr(resource, 'editable') | |
| deletable = s3xrc.model.get_attr(resource, 'deletable') | |
| rss = s3xrc.model.get_attr(resource, 'rss') | |
| else: | |
| record_id = jr.id | |
| href_delete = URL(r=jr.request, f=jr.name, args=['delete', record_id]) | |
| href_edit = URL(r=jr.request, f=jr.name, args=['update', record_id]) | |
| authorised = shn_has_permission('update', table, record_id) | |
| if authorised: | |
| redirect(href_edit) | |
| authorised = shn_has_permission('read', table, record_id) | |
| if authorised: | |
| shn_audit_read(operation='read', module=module, resource=resource, record=record_id, representation=jr.representation) | |
| if jr.representation=="html": | |
| shn_custom_view(jr, 'display.html') | |
| try: | |
| title = s3.crud_strings[jr.tablename].title_display | |
| except: | |
| title = s3.crud_strings.title_display | |
| output = dict(title=title) | |
| if jr.component: | |
| try: | |
| subtitle = s3.crud_strings[tablename].title_display | |
| except: | |
| subtitle = s3.crud_strings.title_display | |
| output.update(subtitle=subtitle) | |
| if pheader: | |
| try: | |
| _pheader = pheader(jr.name, jr.id, jr.representation, next=jr.there(), same=jr.same()) | |
| except: | |
| _pheader = pheader | |
| if _pheader: | |
| output.update(pheader=_pheader) | |
| item = crud.read(table, record_id) | |
| if href_edit and editable: | |
| edit = A(T("Edit"), _href=href_edit, _id='edit-btn') | |
| else: | |
| edit = '' | |
| if href_delete and deletable: | |
| delete = A(T("Delete"), _href=href_delete, _id='delete-btn') | |
| else: | |
| delete = '' | |
| try: | |
| label_list_button = s3.crud_strings[tablename].label_list_button | |
| except: | |
| label_list_button = s3.crud_strings.label_list_button | |
| list_btn = A(label_list_button, _href=jr.there(), _id='list-btn') | |
| output.update(module_name=module_name, item=item, title=title, edit=edit, delete=delete, list_btn=list_btn) | |
| if jr.component and not jr.multiple: | |
| del output["list_btn"] | |
| return(output) | |
| elif jr.representation == "plain": | |
| item = crud.read(table, record_id) | |
| response.view = 'plain.html' | |
| return dict(item=item) | |
| elif jr.representation == "csv": | |
| query = db[table].id == record_id | |
| return export_csv(resource, query) | |
| elif jr.representation == "pdf": # TODO: encoding problems, doesn't quite work | |
| query = db[table].id == record_id | |
| return export_pdf(table, query) | |
| elif jr.representation == "xls": | |
| query = db[table].id == record_id | |
| return export_xls(table, query) | |
| elif jr.representation in shn_json_export_formats: | |
| return export_json(jr) | |
| elif jr.representation in shn_xml_export_formats: | |
| return export_xml(jr) | |
| elif jr.representation == "rss": # TODO: replace by XML export | |
| query = db[table].id == record_id | |
| return export_rss(module, resource, query, rss=rss, linkto=jr.here('html')) | |
| else: | |
| session.error = BADFORMAT | |
| redirect(URL(r=request, f='index')) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': jr.here()})) | |
| # | |
| # shn_list -------------------------------------------------------------------- | |
| # | |
| def shn_list_linkto(field): | |
| """ Helper function to generate links in list views to Display items """ | |
| return URL(r=request, args=[field], | |
| vars={"_next":URL(r=request, args=request.args, vars=request.vars)}) | |
| def shn_list_linkto_update(field): | |
| """ Helper function to generate links in list views to Update items""" | |
| return URL(r=request, args=['update', field], | |
| vars={"_next":URL(r=request, args=request.args, vars=request.vars)}) | |
| def shn_list_jlinkto(field): | |
| """ Helper function to generate links in list views """ | |
| return URL(r=request, args=[request.args[0], request.args[1], field], | |
| vars={"_next":URL(r=request, args=request.args, vars=request.vars)}) | |
| def shn_list_jlinkto_update(field): | |
| """ Helper function to generate links in list views to update items""" | |
| return URL(r=request, args=[request.args[0], request.args[1], 'update', field], | |
| vars={"_next":URL(r=request, args=request.args, vars=request.vars)}) | |
| def shn_list(jr, pheader=None, list_fields=None, listadd=True, main=None, extra=None, orderby=None, sortby=None, onvalidation=None, onaccept=None, rss=None): | |
| """ List records matching the request """ | |
| module, resource, table, tablename = jr.target() | |
| if jr.component: | |
| listadd = jr.component.attr.listadd | |
| if listadd is None: | |
| listadd=True | |
| main, extra = jr.component.attr.main, jr.component.attr.extra | |
| orderby = jr.component.attr.orderby | |
| sortby = jr.component.attr.sortby | |
| onvalidation = jr.component.attr.onvalidation | |
| onaccept = jr.component.attr.onaccept | |
| rss = jr.component.attr.rss | |
| query = shn_accessible_query('read', table) | |
| if jr.record: | |
| query = (table[jr.fkey]==jr.record[jr.pkey]) & query | |
| else: | |
| query = (table[jr.fkey]==jr.table[jr.pkey]) & query | |
| if response.s3.jfilter: | |
| query = response.s3.jfilter & query | |
| if jr.component_id: | |
| query = (table.id==jr.component_id) & query | |
| href_add = URL(r=jr.request, f=jr.name, args=[jr.id, resource, 'create']) | |
| else: | |
| query = shn_accessible_query('read', table) | |
| if response.s3.filter: | |
| query = response.s3.filter & query | |
| href_add = URL(r=jr.request, f=jr.name, args=['create']) | |
| if 'deleted' in table: | |
| query = ((table.deleted == False) | (table.deleted == None)) & query | |
| shn_audit_read(operation='list', | |
| module=module, | |
| resource=resource, | |
| representation=jr.representation) | |
| if jr.representation=="html": | |
| output = dict(module_name=module_name, main=main, extra=extra, sortby=sortby) | |
| if jr.component: | |
| try: | |
| title = s3.crud_strings[jr.tablename].title_display | |
| except: | |
| title = s3.crud_strings.title_display | |
| try: | |
| subtitle = s3.crud_strings[tablename].subtitle_list | |
| except: | |
| subtitle = s3.crud_strings.subtitle_list | |
| if pheader: | |
| try: | |
| _pheader = pheader(jr.name, jr.id, jr.representation, | |
| next=jr.there(), | |
| same=jr.same()) | |
| except: | |
| _pheader = pheader | |
| if _pheader: | |
| output.update(pheader=_pheader) | |
| else: | |
| try: | |
| title = s3.crud_strings[tablename].title_list | |
| except: | |
| title = s3.crud_strings.title_list | |
| try: | |
| subtitle = s3.crud_strings[tablename].subtitle_list | |
| except: | |
| subtitle = s3.crud_strings.subtitle_list | |
| output.update(title=title, subtitle=subtitle) | |
| # Which fields do we display? | |
| fields = None | |
| if jr.component: | |
| list_fields = jr.component.attr.list_fields | |
| _fields = [jr.component.table[f] for f in jr.component.table.fields if f in list_fields] | |
| if _fields: | |
| fields = [f for f in _fields if f.readable] | |
| else: | |
| fields = None | |
| elif list_fields: | |
| fields = [table[f] for f in list_fields if table[f].readable] | |
| if fields and len(fields)==0: | |
| fields.append(table.id) | |
| if not fields: | |
| fields = [table[f] for f in table.fields if table[f].readable] | |
| # Column labels: use custom or prettified label | |
| headers = dict(map(lambda f: (str(f), f.label), fields)) | |
| authorised = shn_has_permission('update', table) | |
| if jr.component: | |
| if authorised: | |
| linkto = shn_list_jlinkto_update | |
| else: | |
| linkto = shn_list_jlinkto | |
| else: | |
| if authorised: | |
| linkto = shn_list_linkto_update | |
| else: | |
| linkto = shn_list_linkto | |
| items = crud.select(table, query=query, | |
| fields=fields, | |
| orderby=orderby, | |
| headers=headers, | |
| linkto=linkto, | |
| truncate=48, _id='list', _class='display') | |
| if not items: | |
| try: | |
| items = s3.crud_strings[tablename].msg_list_empty | |
| except: | |
| items = s3.crud_strings.msg_list_empty | |
| # Update the Return with common items | |
| output.update(dict(items=items)) | |
| authorised = shn_has_permission('create', table) | |
| if authorised and listadd: | |
| # Block join field | |
| if jr.component: | |
| _comment = table[jr.fkey].comment | |
| table[jr.fkey].comment = None | |
| table[jr.fkey].default = jr.record[jr.pkey] | |
| table[jr.fkey].writable = False | |
| if onaccept: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, 0) and \ | |
| onaccept(form) | |
| else: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, 0) | |
| try: | |
| message = s3.crud_strings[tablename].msg_record_created | |
| except: | |
| message = s3.crud_strings.msg_record_created | |
| # Display the Add form above List | |
| form = crud.create(table, | |
| onvalidation=onvalidation, | |
| onaccept=_onaccept, | |
| message=message, | |
| next=jr.there()) | |
| #form[0].append(TR(TD(), TD(INPUT(_type="reset", _value="Reset form")))) | |
| if response.s3.cancel: | |
| form[0][-1][1].append(INPUT(_type="button", _value="Cancel", _onclick="window.location='%s';" % response.s3.cancel)) | |
| if jr.component: | |
| table[jr.fkey].comment = _comment | |
| try: | |
| addtitle = s3.crud_strings[tablename].subtitle_create | |
| except: | |
| addtitle = s3.crud_strings.subtitle_create | |
| # Check for presence of Custom View | |
| shn_custom_view(jr, 'list_create.html') | |
| # Add specificities to Return | |
| output.update(dict(form=form, addtitle=addtitle)) | |
| else: | |
| # List only with create button below | |
| if listadd: | |
| try: | |
| label_create_button = s3.crud_strings[tablename].label_create_button | |
| except: | |
| label_create_button = s3.crud_strings.label_create_button | |
| add_btn = A(label_create_button, _href=href_add, _id='add-btn') | |
| else: | |
| add_btn = '' | |
| # Add specificities to Return | |
| output.update(dict(add_btn=add_btn)) | |
| # Check for presence of Custom View | |
| shn_custom_view(jr, 'list.html') | |
| return output | |
| elif jr.representation=="ext": | |
| output = dict(module_name=module_name, main=main, extra=extra, sortby=sortby) | |
| if jr.component: | |
| try: | |
| title = s3.crud_strings[jr.tablename].title_display | |
| except: | |
| title = s3.crud_strings.title_display | |
| else: | |
| try: | |
| title = s3.crud_strings[tablename].title_list | |
| except: | |
| title = s3.crud_strings.title_list | |
| # Add to Return | |
| output.update(title=title) | |
| # Block join field | |
| if jr.component: | |
| _comment = table[jr.fkey].comment | |
| table[jr.fkey].comment = None | |
| table[jr.fkey].default = jr.record[jr.pkey] | |
| table[jr.fkey].writable = False | |
| if onaccept: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, form.vars.id) and \ | |
| onaccept(form) | |
| else: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, form.vars.id) | |
| try: | |
| message = s3.crud_strings[tablename].msg_record_created | |
| except: | |
| message = s3.crud_strings.msg_record_created | |
| # Form is used to build the initial list view | |
| form = crud.create(table, | |
| onvalidation=onvalidation, | |
| onaccept=_onaccept, | |
| message=message, | |
| next=jr.there()) | |
| if jr.component: | |
| table[jr.fkey].comment = _comment | |
| # Update the Return with the Form | |
| output.update(form=form, pagesize=ROWSPERPAGE) | |
| authorised = shn_has_permission('update', table) | |
| if authorised: | |
| # Provide an Editable Grid | |
| # Check for presence of Custom View | |
| shn_custom_view(jr, 'list_create.html', format='ext') | |
| else: | |
| # Provide a read-only Ext grid | |
| # (We could do this from the HTML table using TableGrid, but then we wouldn't have client-side pagination) | |
| if listadd: | |
| try: | |
| label_create_button = s3.crud_strings[tablename].label_create_button | |
| except: | |
| label_create_button = s3.crud_strings.label_create_button | |
| add_btn = A(label_create_button, _href=href_add, _id='add-btn') | |
| else: | |
| add_btn = '' | |
| # Add to Return | |
| output.update(dict(add_btn=add_btn)) | |
| # Check for presence of Custom View | |
| shn_custom_view(jr, 'list.html', format='ext') | |
| return output | |
| elif jr.representation == "plain": | |
| items = crud.select(table, query, truncate=24) | |
| response.view = 'plain.html' | |
| return dict(item=items) | |
| elif jr.representation == "csv": | |
| return export_csv(resource, query) | |
| elif jr.representation == "pdf": | |
| return export_pdf(table, query) | |
| elif jr.representation == "xls": | |
| return export_xls(table, query) | |
| elif jr.representation in shn_json_export_formats: | |
| return export_json(jr) | |
| elif jr.representation in shn_xml_export_formats: | |
| return export_xml(jr) | |
| elif jr.representation == "rss": | |
| return export_rss(module, resource, query, rss=rss, linkto=jr.there('html')) | |
| else: | |
| session.error = BADFORMAT | |
| redirect(URL(r=request, f='index')) | |
| # | |
| # shn_create ------------------------------------------------------------------ | |
| # | |
| def shn_create(jr, pheader=None, onvalidation=None, onaccept=None, main=None): | |
| """ Create new records """ | |
| module, resource, table, tablename = jr.target() | |
| if jr.component: | |
| onvalidation = s3xrc.model.get_attr(resource, "onvalidation") | |
| onaccept = s3xrc.model.get_attr(resource, "onaccept") | |
| main = s3xrc.model.get_attr(resource, "main") | |
| extra = s3xrc.model.get_attr(resource, "extra") | |
| if jr.representation == "html": | |
| # Check for presence of Custom View | |
| shn_custom_view(jr, 'create.html') | |
| output = dict(module_name=module_name, module=module, resource=resource, main=main) | |
| if jr.component: | |
| try: | |
| title = s3.crud_strings[jr.tablename].title_display | |
| except: | |
| title = s3.crud_strings.title_display | |
| try: | |
| subtitle = s3.crud_strings[tablename].subtitle_create | |
| except: | |
| subtitle = s3.crud_strings.subtitle_create | |
| output.update(subtitle=subtitle) | |
| if pheader: | |
| try: | |
| _pheader = pheader(jr.name, jr.id, jr.representation, | |
| next=jr.there(), | |
| same=jr.same()) | |
| except: | |
| _pheader = pheader | |
| if _pheader: | |
| output.update(pheader=_pheader) | |
| else: | |
| try: | |
| title = s3.crud_strings[tablename].title_create | |
| except: | |
| title = s3.crud_strings.title_create | |
| try: | |
| label_list_button = s3.crud_strings[tablename].label_list_button | |
| except: | |
| label_list_button = s3.crud_strings.label_list_button | |
| list_btn = A(label_list_button, _href=jr.there(), _id='list-btn') | |
| output.update(title=title, list_btn=list_btn) | |
| if jr.component: | |
| # Block join field | |
| _comment = table[jr.fkey].comment | |
| table[jr.fkey].comment = None | |
| table[jr.fkey].default = jr.record[jr.pkey] | |
| table[jr.fkey].writable = False | |
| # Save callbacks | |
| create_onvalidation = crud.settings.create_onvalidation | |
| create_onaccept = crud.settings.create_onaccept | |
| create_next = crud.settings.create_next | |
| # Neutralize callbacks | |
| crud.settings.create_onvalidation = None | |
| crud.settings.create_onaccept = None | |
| crud.settings.create_next = s3xrc.model.get_attr(jr.component_name, 'create_next') or \ | |
| jr.there() | |
| else: | |
| if not crud.settings.create_next: | |
| crud.settings.create_next = jr.there() | |
| if onaccept: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, 0) and \ | |
| onaccept(form) | |
| else: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, 0) | |
| try: | |
| message = s3.crud_strings[tablename].msg_record_created | |
| except: | |
| message = s3.crud_strings.msg_record_created | |
| form = crud.create(table, | |
| message=message, | |
| onvalidation=onvalidation, | |
| onaccept=_onaccept) | |
| #form[0].append(TR(TD(), TD(INPUT(_type="reset", _value="Reset form")))) | |
| if response.s3.cancel: | |
| form[0][-1][1].append(INPUT(_type="button", _value="Cancel", _onclick="window.location='%s';" % response.s3.cancel)) | |
| if jr.component: | |
| # Restore comment | |
| table[jr.fkey].comment = _comment | |
| # Restore callbacks | |
| crud.settings.create_onvalidation = create_onvalidation | |
| crud.settings.create_onaccept = create_onaccept | |
| crud.settings.create_next = create_next | |
| output.update(form=form) | |
| if jr.component and not jr.multiple: | |
| del output["list_btn"] | |
| return output | |
| elif jr.representation == "plain": | |
| if onaccept: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| onaccept(form) | |
| else: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) | |
| form = crud.create(table, onvalidation=onvalidation, onaccept=_onaccept) | |
| response.view = 'plain.html' | |
| return dict(item=form) | |
| elif jr.representation == "ext": | |
| shn_custom_view(jr, 'create.html', format='ext') | |
| return dict() | |
| elif jr.representation == "popup": | |
| if onaccept: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) and \ | |
| onaccept(form) | |
| else: | |
| _onaccept = lambda form: \ | |
| shn_audit_create(form, module, resource, jr.representation) | |
| form = crud.create(table, onvalidation=onvalidation, onaccept=_onaccept) | |
| # Check for presence of Custom View | |
| shn_custom_view(jr, 'popup.html') | |
| return dict(module_name=module_name, form=form, module=module, | |
| resource=resource, main=main, caller=request.vars.caller) | |
| elif jr.representation == "url": | |
| return import_url(jr, table, method="create", onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation == "csv": | |
| # Read in POST | |
| file = request.vars.filename.file | |
| try: | |
| import_csv(file, table) | |
| session.flash = T('Data uploaded') | |
| except: | |
| session.error = T('Unable to parse CSV file!') | |
| redirect(jr.there()) | |
| elif jr.representation in shn_json_import_formats: | |
| response.view = 'plain.html' | |
| return import_json(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation in shn_xml_import_formats: | |
| response.view = 'plain.html' | |
| return import_xml(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| else: | |
| session.error = BADFORMAT | |
| redirect(URL(r=request, f='index')) | |
| # | |
| # shn_update ------------------------------------------------------------------ | |
| # | |
| def shn_update(jr, pheader=None, deletable=True, onvalidation=None, onaccept=None): | |
| """ Update an existing record """ | |
| module, resource, table, tablename = jr.target() | |
| if jr.component: | |
| if jr.multiple and not jr.component_id: | |
| return shn_create(jr, pheader) | |
| query = (table[jr.fkey]==jr.record[jr.pkey]) | |
| if jr.component_id: | |
| query = (table.id==jr.component_id) & query | |
| if 'deleted' in table: | |
| query = ((table.deleted==False) | (table.deleted==None)) & query | |
| try: | |
| record_id = db(query).select(table.id)[0].id | |
| except: | |
| record_id = None | |
| href_delete = None | |
| href_edit = None | |
| session.error = BADRECORD | |
| redirect(jr.there()) | |
| onvalidation = s3xrc.model.get_attr(resource, 'update_onvalidation') | |
| onaccept = s3xrc.model.get_attr(resource, 'update_onaccept') | |
| deletable = s3xrc.model.get_attr(resource, 'deletable') | |
| else: | |
| record_id = jr.id | |
| deletable = shn_has_permission('delete', table, record_id) | |
| authorised = shn_has_permission('update', table, record_id) | |
| if authorised: | |
| crud.settings.update_deletable = deletable | |
| if jr.representation == "html": | |
| shn_custom_view(jr, 'update.html') | |
| output = dict(module_name=module_name) | |
| if jr.component: | |
| try: | |
| title = s3.crud_strings[jr.tablename].title_display | |
| except: | |
| title = s3.crud_strings.title_display | |
| try: | |
| subtitle = s3.crud_strings[tablename].title_update | |
| except: | |
| subtitle = s3.crud_strings.title_update | |
| output.update(subtitle=subtitle) | |
| if pheader: | |
| try: | |
| _pheader = pheader(jr.name, jr.id, jr.representation, | |
| next=jr.there(), | |
| same=jr.same()) | |
| except: | |
| _pheader = pheader | |
| if _pheader: | |
| output.update(pheader=_pheader) | |
| else: | |
| try: | |
| title = s3.crud_strings[tablename].title_update | |
| except: | |
| title = s3.crud_strings.title_update | |
| try: | |
| label_list_button = s3.crud_strings[tablename].label_list_button | |
| except: | |
| label_list_button = s3.crud_strings.label_list_button | |
| list_btn = A(label_list_button, _href=jr.there(), _id='list-btn') | |
| output.update(title=title, list_btn=list_btn) | |
| if jr.component: | |
| # Block join field | |
| _comment = table[jr.fkey].comment | |
| table[jr.fkey].comment = None | |
| table[jr.fkey].default = jr.record[jr.pkey] | |
| table[jr.fkey].writable = False | |
| # Save callbacks | |
| update_onvalidation = crud.settings.update_onvalidation | |
| update_onaccept = crud.settings.update_onaccept | |
| update_next = crud.settings.update_next | |
| # Neutralize callbacks | |
| crud.settings.update_onvalidation = None | |
| crud.settings.update_onaccept = None | |
| crud.settings.update_next = s3xrc.model.get_attr(jr.component_name, 'update_next') or \ | |
| jr.there() | |
| else: | |
| if not crud.settings.update_next: | |
| crud.settings.update_next = jr.here() | |
| try: | |
| message = s3.crud_strings[tablename].msg_record_modified | |
| except: | |
| message = s3.crud_strings.msg_record_modified | |
| if onaccept: | |
| _onaccept = lambda form: \ | |
| shn_audit_update(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, form.vars.id) and \ | |
| onaccept(form) | |
| else: | |
| _onaccept = lambda form: \ | |
| shn_audit_update(form, module, resource, jr.representation) and \ | |
| s3xrc.store_session(session, module, resource, form.vars.id) | |
| form = crud.update(table, record_id, | |
| message=message, | |
| onvalidation=onvalidation, | |
| onaccept=_onaccept, | |
| deletable=False) # TODO: add extra delete button to form | |
| #form[0].append(TR(TD(), TD(INPUT(_type="reset", _value="Reset form")))) | |
| if response.s3.cancel: | |
| form[0][-1][1].append(INPUT(_type="button", _value="Cancel", _onclick="window.location='%s';" % response.s3.cancel)) | |
| if jr.component: | |
| # Restore comment | |
| table[jr.fkey].comment = _comment | |
| # Restore callbacks | |
| crud.settings.update_onvalidation = update_onvalidation | |
| crud.settings.update_onaccept = update_onaccept | |
| crud.settings.update_next = update_next | |
| output.update(form=form) | |
| if jr.component and not jr.multiple: | |
| del output["list_btn"] | |
| return(output) | |
| elif jr.representation == "plain": | |
| if onaccept: | |
| _onaccept = lambda form: \ | |
| shn_audit_update(form, module, resource, jr.representation) and \ | |
| onaccept(form) | |
| else: | |
| _onaccept = lambda form: \ | |
| shn_audit_update(form, module, resource, jr.representation) | |
| form = crud.update(table, record_id, | |
| onvalidation=onvalidation, | |
| onaccept=_onaccept, | |
| deletable=False) | |
| response.view = 'plain.html' | |
| return dict(item=form) | |
| elif jr.representation == "ext": | |
| shn_custom_view(jr, 'update.html', format='ext') | |
| return dict() | |
| elif jr.representation == "url": | |
| return import_url(jr, table, method="update", onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation in shn_json_import_formats: | |
| response.view = 'plain.html' | |
| return import_json(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation in shn_xml_import_formats: | |
| response.view = 'plain.html' | |
| return import_xml(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| else: | |
| session.error = BADFORMAT | |
| redirect(URL(r=request, f='index')) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': jr.here()})) | |
| # | |
| # shn_delete ------------------------------------------------------------------ | |
| # | |
| def shn_delete(jr): | |
| """ Delete record(s) """ | |
| module, resource, table, tablename = jr.target() | |
| if jr.component: | |
| onvalidation = s3xrc.model.get_attr(resource, "delete_onvalidation") | |
| onaccept = s3xrc.model.get_attr(resource, "delete_onaccept") | |
| query = (table[jr.fkey]==jr.record[jr.pkey]) | |
| if jr.component_id: | |
| query = (table.id==jr.component_id) & query | |
| if 'deleted' in table: | |
| query = (table.deleted==False) & query | |
| else: | |
| query = (table.id==jr.id) | |
| if 'deleted' in table: | |
| query = (table.deleted==False) & query | |
| # Get target records | |
| rows = db(query).select(table.ALL) | |
| # Nothing to do? Return here! | |
| if not rows or len(rows)==0: | |
| session.confirmation = T('No records to delete') | |
| return | |
| try: | |
| message = s3.crud_strings[tablename].msg_record_deleted | |
| except: | |
| message = s3.crud_strings.msg_record_deleted | |
| if jr.component: | |
| # Save callback settings | |
| delete_onvalidation = crud.settings.delete_onvalidation | |
| delete_onaccept = crud.settings.delete_onaccept | |
| delete_next = crud.settings.delete_next | |
| # Set resource specific callbacks, if any | |
| crud.settings.delete_onvalidation = onvalidation | |
| crud.settings.delete_onaccept = onaccept | |
| crud.settings.delete_next = None # do not set here! | |
| # Delete all accessible records | |
| numrows = 0 | |
| for row in rows: | |
| if shn_has_permission('delete', table, row.id): | |
| numrows += 1 | |
| shn_audit_delete(module, resource, row.id, jr.representation) | |
| if "deleted" in db[table] and \ | |
| db(db.s3_setting.id==1).select()[0].archive_not_delete: | |
| if crud.settings.delete_onvalidation: | |
| crud.settings.delete_onvalidation(row) | |
| db(db[table].id == row.id).update(deleted = True) | |
| if crud.settings.delete_onaccept: | |
| crud.settings.delete_onaccept(row) | |
| else: | |
| if jr.representation == "ajax": | |
| crud.settings.delete_next = jr.there(representation=jr.representation) | |
| crud.delete(table, row.id) | |
| else: | |
| continue | |
| if jr.component: | |
| # Restore callback settings | |
| crud.settings.delete_onvalidation = delete_onvalidation | |
| crud.settings.delete_onaccept = delete_onaccept | |
| crud.settings.delete_next = delete_next | |
| delete_next = jr.component.attr.delete_next | |
| # Confirm and return | |
| if numrows > 1: | |
| session.confirmation = "%s %s" % ( numrows, T('records deleted')) | |
| else: | |
| session.confirmation = message | |
| if jr.component and delete_next: # but redirect here! | |
| redirect(delete_next) | |
| return | |
| # | |
| # shn_options ----------------------------------------------------------------- | |
| # | |
| def shn_options(jr): | |
| if jr.representation=="xml": | |
| response.headers["Content-Type"] = "text/xml" | |
| response.view = "plain.html" | |
| return jr.options_xml(pretty_print=PRETTY_PRINT) | |
| elif jr.representation=="json": | |
| response.headers['Content-Type'] = 'text/x-json' | |
| response.view = "plain.html" | |
| return jr.options_json(pretty_print=PRETTY_PRINT) | |
| else: | |
| session.error = BADFORMAT | |
| redirect(URL(r=request, f='index')) | |
| # ***************************************************************************** | |
| # Main controller function | |
| # | |
| # shn_rest_controller --------------------------------------------------------- | |
| # | |
| def shn_rest_controller(module, resource, | |
| deletable=True, | |
| editable=True, | |
| listadd=True, | |
| list_fields=None, | |
| main='name', | |
| extra=None, | |
| orderby=None, | |
| sortby=None, | |
| pheader=None, | |
| rss=None, | |
| onvalidation=None, | |
| onaccept=None): | |
| """ | |
| RESTlike controller function | |
| ============================ | |
| Provides CRUD operations for the given module/resource. | |
| Supported Representations: | |
| ========================== | |
| Representation is recognized from the extension of the target resource. | |
| You can still pass a "?format=" to override this. | |
| - B{html}: is the default (including full layout) | |
| - B{plain}: is HTML with no layout | |
| - can be inserted into DIVs via AJAX calls | |
| - can be useful for clients on low-bandwidth or small screen sizes | |
| - B{ext}: is Ext layouts (experimental) | |
| - B{json}: JSON export/import using XSLT | |
| - B{xml}: XML export/import using XSLT | |
| - B{csv}: useful for synchronization/database migration | |
| - List/Display/Create for now | |
| - B{pdf}: list/read only | |
| - B{rss}: list only | |
| - B{xls}: list/read only | |
| - B{ajax}: designed to be run asynchronously to refresh page elements | |
| - B{url}: designed to be accessed via JavaScript | |
| - responses in JSON format | |
| - create/update/delete done via simple GET vars (no form displayed) | |
| - B{popup}: designed to be used inside popups | |
| Request options: | |
| ================ | |
| - B{response.s3.filter}: contains custom query to filter list views (primary resources) | |
| - B{response.s3.jfilter}: contains custom query to filter list views (joined resources) | |
| - B{response.s3.cancel}: adds a cancel button to forms & provides a location to direct to upon pressing | |
| Description: | |
| ============ | |
| @param deletable: provide visible options for deletion (optional, default=True) | |
| @param editable: provide visible options for editing (optional, default=True) | |
| @param listadd: provide an add form in the list view (optional, default=True) | |
| @param main: | |
| the field used for the title in RSS output (optional, default="name") | |
| @param extra: | |
| the field used for the description in RSS output & in Search AutoComplete | |
| (optional, default=None) | |
| @param orderby: | |
| the sort order for server-side paginated list views (optional, default=None), e.g.:: | |
| db.mytable.myfield1|db.mytable.myfield2 | |
| @param sortby: | |
| the sort order for client-side paginated list views (optional, default=None), e.g.:: | |
| [[1, "asc"], [2, "desc"]] | |
| see: U{http://datatables.net/examples/basic_init/table_sorting.html} | |
| @param onvalidation: callback processed B{before} DB IO | |
| @type onvalidation: | |
| lambda form:, function(form) | |
| @param onaccept: callback processed B{after} DB IO | |
| @type onaccept: | |
| lambda form:, function(form) | |
| @param pheader: function to produce a page header for the primary resource | |
| @type pheader: | |
| function(resource, record_id, representation, next=None, same=None) | |
| @author: Fran Boon | |
| @author: nursix | |
| @todo: | |
| - Alternate Representations | |
| - CSV update | |
| - SMS, LDIF | |
| """ | |
| # Parse original request -------------------------------------------------- | |
| jr = s3xrc.request(module, resource, request, session=session) | |
| # Invalid request? | |
| if jr.invalid: | |
| if jr.badmethod: | |
| #session.error = BADMETHOD | |
| raise HTTP(501, body=BADMETHOD) | |
| elif jr.badrecord: | |
| #session.error = BADRECORD | |
| raise HTTP(404, body=BADRECORD) | |
| else: | |
| #session.error = INVALIDREQUEST | |
| raise HTTP(400, body=INVALIDREQUEST) | |
| #redirect(URL(r=request, c=request.controller, f='index')) | |
| # Get backlinks | |
| here, there, same = jr.here(), jr.there(), jr.same() | |
| # Check read permission on primary table | |
| if not shn_has_permission('read', jr.table): | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Record ID is required in joined-table operations and read action: | |
| if not jr.id and (jr.component or jr.method=="read") and not jr.method=="options" and not "select" in jr.request.vars: | |
| # TODO: Cleanup - this is PR specific | |
| if jr.prefix=="pr" and jr.name=="person" and jr.representation=='html': | |
| redirect(URL(r=request, f='person', args='search_simple', vars={"_next": same})) | |
| else: | |
| raise HTTP(404, body=BADRECORD) | |
| #session.error = BADRECORD | |
| #redirect(URL(r=request, f='index')) | |
| # ************************************************************************* | |
| # Joined Table Operation | |
| if jr.component: | |
| if jr.method and jr.custom_action: | |
| try: | |
| return(jr.custom_action(jr, onvalidation=None, onaccept=None)) | |
| except: | |
| raise HTTP(500) | |
| # HTTP Multi-Record Operation ***************************************** | |
| if jr.method==None and jr.multiple and not jr.component_id: | |
| # HTTP List/List-add ---------------------------------------------- | |
| if jr.http=='GET': | |
| authorised = shn_has_permission('read', jr.component.table) | |
| if authorised: | |
| return shn_list(jr, pheader, rss=rss) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here })) | |
| # HTTP Create ----------------------------------------------------- | |
| elif jr.http=='PUT' or jr.http=='POST': | |
| if jr.representation in shn_json_import_formats: | |
| response.view = 'plain.html' | |
| return import_json(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation in shn_xml_import_formats: | |
| response.view = 'plain.html' | |
| return import_xml(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.http == "POST": | |
| authorised = shn_has_permission('read', jr.component.table) | |
| if authorised: | |
| return shn_list(jr, pheader, rss=rss) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here })) | |
| else: | |
| raise HTTP(501, body=BADFORMAT) | |
| # HTTP Delete ----------------------------------------------------- | |
| elif jr.http=='DELETE': | |
| # Not implemented | |
| raise HTTP(501) | |
| # Unsupported HTTP method ----------------------------------------- | |
| else: | |
| # Unsupported HTTP method for this context: | |
| # HEAD, OPTIONS, TRACE, CONNECT | |
| # Not implemented | |
| raise HTTP(501) | |
| # HTTP Single-Record Operation **************************************** | |
| elif jr.method==None and (jr.component_id or not jr.multiple): | |
| # HTTP Read/Update ------------------------------------------------ | |
| if jr.http=='GET': | |
| authorised = shn_has_permission('read', jr.component.table) | |
| if authorised: | |
| return shn_read(jr, pheader=pheader, rss=rss) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here })) | |
| # HTTP Update ----------------------------------------------------- | |
| elif jr.http=='PUT' or jr.http == "POST": | |
| if jr.representation in shn_json_import_formats: | |
| response.view = 'plain.html' | |
| return import_json(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation in shn_xml_import_formats: | |
| response.view = 'plain.html' | |
| return import_xml(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.http == "POST": | |
| authorised = shn_has_permission('read', jr.component.table) | |
| if authorised: | |
| return shn_read(jr, pheader=pheader, rss=rss) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here })) | |
| else: | |
| raise HTTP(501, body=BADFORMAT) | |
| # HTTP Delete ----------------------------------------------------- | |
| elif jr.http=='DELETE': | |
| # Not implemented | |
| raise HTTP(501) | |
| # Unsupported HTTP method ----------------------------------------- | |
| else: | |
| # Unsupported HTTP method for this context: | |
| # POST, HEAD, OPTIONS, TRACE, CONNECT | |
| # Not implemented | |
| raise HTTP(501) | |
| # Create (joined table) ************************************************ | |
| elif jr.method=="create": | |
| authorised = shn_has_permission(jr.method, jr.component.table) | |
| if authorised: | |
| return shn_create(jr, pheader) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Read (joined table) ************************************************* | |
| elif jr.method=="read" or jr.method=="display": | |
| authorised = shn_has_permission('read', jr.component.table) | |
| if authorised: | |
| if jr.multiple and not jr.component_id: | |
| # This is a list action | |
| return shn_list(jr, pheader, rss=rss) | |
| else: | |
| # This is a read action | |
| return shn_read(jr, pheader, rss=rss) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here })) | |
| # Update (joined table) *********************************************** | |
| elif jr.method=="update": | |
| authorised = shn_has_permission(jr.method, jr.component.table) | |
| if authorised: | |
| return shn_update(jr, pheader) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Delete (joined table) *********************************************** | |
| elif jr.method=="delete": | |
| authorised = shn_has_permission(jr.method, jr.component.table) | |
| if authorised: | |
| shn_delete(jr) | |
| redirect(there) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Options (joined table) ********************************************** | |
| elif jr.method=="options": | |
| return shn_options(jr) | |
| # Unsupported Method ************************************************** | |
| else: | |
| raise HTTP(501, body=BADMETHOD) | |
| #session.error = BADMETHOD | |
| #redirect(URL(r=request, f='index')) | |
| # ************************************************************************* | |
| # Single Table Operation | |
| # | |
| else: | |
| # Custom Method ******************************************************* | |
| if jr.method and jr.custom_action: | |
| try: | |
| return(jr.custom_action(jr, onvalidation=onvalidation, onaccept=onaccept)) | |
| except: | |
| raise HTTP(500) | |
| # Clear Session ******************************************************* | |
| elif jr.method=="clear": | |
| # Clear session | |
| s3xrc.clear_session(session, jr.prefix, jr.name) | |
| if '_next' in request.vars: | |
| request_vars = dict(_next=request.vars._next) | |
| else: | |
| request_vars = {} | |
| # Redirect to search | |
| # TODO: build a generic search function, this here is PR specific | |
| if jr.prefix=="pr" and jr.name=="person" and jr.representation=="html": | |
| redirect(URL(r=request, f='person', args='search_simple', vars=request_vars)) | |
| else: | |
| redirect(URL(r=request, c='pr', f=jr.name)) | |
| # HTTP Multi-Record Operation ***************************************** | |
| elif not jr.method and not jr.id: | |
| # HTTP List or List-Add ------------------------------------------- | |
| if jr.http == 'GET': | |
| return shn_list(jr, pheader, list_fields=list_fields, | |
| listadd=listadd, | |
| main=main, | |
| extra=extra, | |
| orderby=orderby, | |
| sortby=sortby, | |
| onvalidation=onvalidation, | |
| onaccept=onaccept, | |
| rss=rss) | |
| # HTTP Create ----------------------------------------------------- | |
| elif jr.http == 'PUT' or jr.http == "POST": | |
| # http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6 | |
| if jr.representation in shn_json_import_formats: | |
| response.view = 'plain.html' | |
| return import_json(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation in shn_xml_import_formats: | |
| response.view = 'plain.html' | |
| return import_xml(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.http == "POST": | |
| return shn_list(jr, pheader, list_fields=list_fields, | |
| listadd=listadd, | |
| main=main, | |
| extra=extra, | |
| orderby=orderby, | |
| sortby=sortby, | |
| onvalidation=onvalidation, | |
| onaccept=onaccept, | |
| rss=rss) | |
| else: | |
| raise HTTP(501, body=BADFORMAT) | |
| # Unsupported HTTP method ----------------------------------------- | |
| else: | |
| # Unsupported HTTP method for this context: | |
| # DELETE, HEAD, OPTIONS, TRACE, CONNECT | |
| # Not implemented | |
| raise HTTP(501) | |
| # HTTP Single Record Operation **************************************** | |
| elif jr.id and not jr.method: | |
| # HTTP Read (single record) --------------------------------------- | |
| if jr.http == 'GET': | |
| return shn_read(jr, pheader=pheader, editable=editable, deletable=deletable, rss=rss) | |
| # HTTP Create/Update (single record) ------------------------------ | |
| elif jr.http == 'PUT' or jr.http == "POST": | |
| # http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6 | |
| if jr.representation in shn_json_import_formats: | |
| response.view = 'plain.html' | |
| return import_json(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.representation in shn_xml_import_formats: | |
| response.view = 'plain.html' | |
| return import_xml(jr, onvalidation=onvalidation, onaccept=onaccept) | |
| elif jr.http == "POST": | |
| return shn_read(jr, pheader=pheader, editable=editable, deletable=deletable, rss=rss) | |
| else: | |
| raise HTTP(501, body=BADFORMAT) | |
| # HTTP Delete (single record) ------------------------------------- | |
| elif jr.http == 'DELETE': | |
| # http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7 | |
| if db(db[jr.table].id == jr.id).select(): | |
| authorised = shn_has_permission('delete', jr.table, jr.id) | |
| if authorised: | |
| shn_delete(jr) | |
| item = json_message() | |
| response.view = 'plain.html' | |
| return dict(item=item) | |
| else: | |
| # Unauthorised | |
| raise HTTP(401) | |
| else: | |
| # Not found | |
| raise HTTP(404) | |
| # Unsupported HTTP method ----------------------------------------- | |
| else: | |
| # Unsupported HTTP method for this context: | |
| # POST, HEAD, OPTIONS, TRACE, CONNECT | |
| # Not implemented | |
| raise HTTP(501) | |
| # Create (single table) *********************************************** | |
| elif jr.method == "create": | |
| authorised = shn_has_permission(jr.method, jr.table) | |
| if authorised: | |
| return shn_create(jr, pheader, onvalidation=onvalidation, | |
| onaccept=onaccept, main=main) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Read (single table) ************************************************* | |
| elif jr.method == "read" or jr.method == "display": | |
| request.args.remove(jr.method) | |
| redirect(URL(r=request, args=request.args, vars=request.vars)) | |
| # Update (single table) *********************************************** | |
| elif jr.method == "update": | |
| authorised = shn_has_permission(jr.method, jr.table, jr.id) | |
| if authorised: | |
| return shn_update(jr, pheader, deletable=deletable, | |
| onvalidation=onvalidation, onaccept=onaccept) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Delete (single table) *********************************************** | |
| elif jr.method == "delete": | |
| authorised = shn_has_permission(jr.method, jr.table, jr.id) | |
| if authorised: | |
| shn_delete(jr) | |
| redirect(there) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Search (single table) *********************************************** | |
| elif jr.method == "search": | |
| authorised = shn_has_permission('read', jr.table) | |
| if authorised: | |
| # Filter Search list to just those records which user can read | |
| query = shn_accessible_query('read', jr.table) | |
| # Filter search to items which aren't deleted | |
| if 'deleted' in jr.table: | |
| query = (jr.table.deleted==False) & query | |
| # Audit | |
| shn_audit_read(operation='search', module=jr.prefix, | |
| resource=jr.name, representation=jr.representation) | |
| if jr.representation == "html": | |
| shn_represent(jr.table, jr.prefix, jr.name, deletable, main, extra) | |
| search = t2.search(jr.table, query=query) | |
| # Check for presence of Custom View | |
| shn_custom_view(jr, 'search.html') | |
| # CRUD Strings | |
| title = s3.crud_strings.title_search | |
| return dict(module_name=module_name, search=search, title=title) | |
| if jr.representation == "json": | |
| # JQuery Autocomplete uses 'q' instead of 'value' | |
| value = request.vars.value or request.vars.q or None | |
| if request.vars.field and request.vars.filter and value: | |
| field = str.lower(request.vars.field) | |
| filter = request.vars.filter | |
| if filter == '~': | |
| query = query & (jr.table[field].like('%' + value + '%')) | |
| limit = int(request.vars.limit) or None | |
| if limit: | |
| item = db(query).select(limitby=(0, limit)).json() | |
| else: | |
| item = db(query).select().json() | |
| elif filter == '=': | |
| query = query & (jr.table[field] == value) | |
| item = db(query).select().json() | |
| elif filter == '<': | |
| query = query & (jr.table[field] < value) | |
| item = db(query).select().json() | |
| elif filter == '>': | |
| query = query & (jr.table[field] > value) | |
| item = db(query).select().json() | |
| else: | |
| item = json_message(False, 400, "Unsupported filter! Supported filters: ~, =, <, >") | |
| raise HTTP(400, body=item) | |
| else: | |
| item = json_message(False, 400, "Search requires specifying Field, Filter & Value!") | |
| raise HTTP(400, body=item) | |
| response.view = 'plain.html' | |
| return dict(item=item) | |
| else: | |
| raise HTTP(501, body=BADFORMAT) | |
| #session.error = BADFORMAT | |
| #redirect(URL(r=request)) | |
| else: | |
| session.error = UNAUTHORISED | |
| redirect(URL(r=request, c='default', f='user', args='login', vars={'_next': here})) | |
| # Options (single table) ********************************************** | |
| elif jr.method=="options": | |
| return shn_options(jr) | |
| # Unsupported Method ************************************************** | |
| else: | |
| raise HTTP(501, body=BADMETHOD) | |
| #session.error = BADMETHOD | |
| #redirect(URL(r=request)) | |
| # END | |
| # ***************************************************************************** | |
| Powered by web2py (TM) created by Massimo Di Pierro © 2007 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment