~sajanravindran-deactivatedaccount/sahana-eden/defect-195

« back to all changes in this revision

Viewing changes to modules/s3/s3widgets.py

  • Committer: Fran Boon
  • Date: 2011-03-29 22:38:41 UTC
  • Revision ID: fran@aidiq.com-20110329223841-x974rgep64v62nja
pre-merge commit: New Location Selector work-in-progress with associated fixes & minor enhancements

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
           "S3LocationAutocompleteWidget",
42
42
           "S3PersonAutocompleteWidget",
43
43
           "S3LocationSelectorWidget",
44
 
           "S3StreetAddressWidget",
45
44
           "S3CheckboxesWidget",
46
45
           "S3MultiSelectWidget",
47
46
           "S3ACLWidget",
408
407
 
409
408
    def __call__(self ,field, value, **attributes):
410
409
 
 
410
        request = self.request
 
411
 
411
412
        default = dict(
412
413
            _type = "text",
413
414
            value = (value != None and str(value)) or "",
420
421
        real_input = str(field).replace(".", "_")
421
422
        dummy_input = "dummy_%s" % real_input
422
423
        fieldname = self.fieldname
423
 
        url = URL(r=self.request, c=self.prefix, f=self.resourcename,
 
424
        url = URL(r=request, c=self.prefix, f=self.resourcename,
424
425
                  args="search.json",
425
426
                  vars={"filter":"~",
426
427
                        "field":fieldname,
555
556
 
556
557
    def __call__(self ,field, value, **attributes):
557
558
 
 
559
        request = self.request
 
560
 
558
561
        default = dict(
559
562
            _type = "text",
560
563
            value = (value != None and str(value)) or "",
566
569
 
567
570
        real_input = str(field).replace(".", "_")
568
571
        dummy_input = "dummy_%s" % real_input
569
 
        url = URL(r=self.request, c="pr", f="person",
 
572
        url = URL(r=request, c="pr", f="person",
570
573
                  args="search.json",
571
574
                  vars={"filter":"~"})
572
575
 
690
693
 
691
694
 
692
695
# -----------------------------------------------------------------------------
 
696
class S3LocationSelectorWidget2(FormWidget):
 
697
 
 
698
    """
 
699
        Renders a gis_location SELECT to allow inline display/editing of linked fields.
 
700
 
 
701
        Create form
 
702
            Active Tab: Add New Location
 
703
                Country Dropdown (to set the Number & Labels of Hierarchy)
 
704
                Building Name
 
705
                Street Address (Line1/Line2?)
 
706
                    Trigger a geocoder lookup onblur
 
707
                Postcode
 
708
                L2-L5 as Autocompletes which create missing locations automatically
 
709
                L1 as Dropdown? (Have a gis_config setting to inform whether this is populated for a given L0)
 
710
                Inline Map? (Deployment Option?)
 
711
                Lat Lon
 
712
            Inactive Tab: Search Existing Locations
 
713
                Hierarchical Filters above the Search Box
 
714
                    Search is filtered to values shown
 
715
 
 
716
        Update form
 
717
            Update form has uuid set server-side & hence S3.gis.uuid set client-side
 
718
            If location is not shared by other resources:
 
719
                Active Tab: Fields are editable for this location
 
720
                Inactive Tab: Move to an existing Location
 
721
            If location is shared by other resources: <- assume this mode for now
 
722
                Active Tab: Fields are readable for this location
 
723
                Inactive Tab: Edit this shared Location
 
724
                Inactive Tab: Move to an existing Location
 
725
 
 
726
        @author: Fran Boon (fran@aidiq.com)
 
727
 
 
728
        @see: http://eden.sahanafoundation.org/wiki/BluePrintGISLocationSelector
 
729
    """
 
730
 
 
731
    def __init__(self,
 
732
                 db,
 
733
                 gis,
 
734
                 deployment_settings,
 
735
                 request,
 
736
                 response,
 
737
                 T,
 
738
                 ):
 
739
 
 
740
        self.db = db
 
741
        self.gis = gis
 
742
        self.deployment_settings = deployment_settings
 
743
        self.request = request
 
744
        self.response = response
 
745
        self.T = T
 
746
 
 
747
    def __call__(self, field, value, **attributes):
 
748
 
 
749
        db = self.db
 
750
        gis = self.gis
 
751
        deployment_settings = self.deployment_settings
 
752
        request = self.request
 
753
        response = self.response
 
754
        T = self.T
 
755
        locations = db.gis_location
 
756
 
 
757
        # Main Input
 
758
        default = dict(_type = "text",
 
759
                       value = (value != None and str(value)) or "")
 
760
        attr = StringWidget._attributes(field, default, **attributes)
 
761
        # Hide the real field
 
762
        attr["_class"] = "hidden"
 
763
 
 
764
        # Read Options
 
765
        # Are we restricted to just certain countries?
 
766
        countries = response.s3.gis.countries
 
767
        # Should we use a Map-based selector?
 
768
        map_selector = deployment_settings.get_gis_map_selector()
 
769
        # Navigate Away Confirm?
 
770
        if deployment_settings.get_ui_navigate_away_confirm():
 
771
            navigate_away_confirm = "true"
 
772
        else:
 
773
            navigate_away_confirm = "false"
 
774
 
 
775
        # Localised Strings
 
776
        select_location = T("Select a location")
 
777
 
 
778
        map_popup = ""
 
779
        if value:
 
780
            # Read current record
 
781
            this_location = db(locations.id == value).select(locations.uuid,
 
782
                                                             locations.name,
 
783
                                                             locations.level,
 
784
                                                             locations.lat,
 
785
                                                             locations.lon,
 
786
                                                             locations.addr_street,
 
787
                                                             locations.addr_postcode,
 
788
                                                             locations.parent,
 
789
                                                             locations.path,
 
790
                                                             limitby=(0, 1)).first()
 
791
            uuid = this_location.uuid
 
792
            level = this_location.level
 
793
            default[level] = value
 
794
            lat = this_location.lat
 
795
            lon = this_location.lon
 
796
            addr_street = this_location.addr_street or ""
 
797
            addr_street_encoded = ""
 
798
            if addr_street:
 
799
                addr_street_encoded = addr_street.replace("\r\n",
 
800
                                                          "%0d").replace("\r",
 
801
                                                                         "%0d").replace("\n",
 
802
                                                                                        "%0d")
 
803
            postcode = this_location.addr_postcode
 
804
            parent = this_location.parent
 
805
            path = this_location.path
 
806
 
 
807
            # Populate default with Names of ancestors at each level
 
808
            gis.get_parent_per_level(default, value, feature=this_location, names=True)
 
809
 
 
810
            if level:
 
811
                # If within the locations hierarchy then don't populate the visible name box
 
812
                represent = ""
 
813
            else:
 
814
                represent = this_location.name
 
815
 
 
816
            if map_selector:
 
817
                config = gis.get_config()
 
818
                zoom = config.zoom
 
819
                if lat is None or lon is None:
 
820
                    map_lat = config.lat
 
821
                    map_lon = config.lon
 
822
                else:
 
823
                    map_lat = lat
 
824
                    map_lon = lon
 
825
 
 
826
                layername = T("Location")
 
827
                popup_label = ""
 
828
                layer = gis.get_feature_layer("gis",
 
829
                                              "location",
 
830
                                              layername,
 
831
                                              popup_label,
 
832
                                              id=value)
 
833
                if layer:
 
834
                    feature_queries = [layer]
 
835
                else:
 
836
                    feature_queries = []
 
837
                map_popup = gis.show_map(
 
838
                                         lat = map_lat,
 
839
                                         lon = map_lon,
 
840
                                         # Same as a single zoom on a cluster
 
841
                                         zoom = zoom + 2,
 
842
                                         feature_queries = feature_queries,
 
843
                                         #add_feature = True,
 
844
                                         #add_feature_active = False,
 
845
                                         toolbar = True,
 
846
                                         collapsed = True,
 
847
                                         search = True,
 
848
                                         window = True,
 
849
                                         window_hide = True
 
850
                                        )
 
851
                # @ToDo: Check if this location is shared by other resources
 
852
                # (If it is then we shouldn't let the user edit without caution)
 
853
                # List of Tables with a location_id reference
 
854
                #tables = locations._referenced_by
 
855
                # Maybe just try hard-deleting the record to trigger an IntegrityError
 
856
                # For now assume it is a shared location
 
857
 
 
858
        else:
 
859
            # No default value
 
860
            uuid = ""
 
861
            represent = ""
 
862
            level = None
 
863
            lat = ""
 
864
            lon = ""
 
865
            addr_street = ""
 
866
            addr_street_encoded = ""
 
867
            postcode = ""
 
868
            if map_selector:
 
869
                map_popup = gis.show_map(
 
870
                                         add_feature = True,
 
871
                                         add_feature_active = True,
 
872
                                         toolbar = True,
 
873
                                         collapsed = True,
 
874
                                         search = True,
 
875
                                         window = True,
 
876
                                         window_hide = True
 
877
                                        )
 
878
            
 
879
        # Which Levels do we have in our hierarchy & what are their initial Labels?
 
880
        # @ToDo: If we have hardcoded country or one from the value then we can lookup what options we should use for that location
 
881
        #if value["L0"]:
 
882
        #elif len(countries) == 1:
 
883
        #else:
 
884
        # Default
 
885
        location_hierarchy = deployment_settings.get_gis_locations_hierarchy()
 
886
 
 
887
        # Components to inject into Form
 
888
        divider = TR(TD(_class="subheading"))
 
889
        section_label = TR(TD(B("%s:" % field.label)))
 
890
        
 
891
        # Tabs to select between the modes
 
892
        add_button = A(T("Create New Location"),
 
893
                       _style="cursor:pointer; cursor:hand",
 
894
                       _id="gis_location_add-btn")
 
895
 
 
896
        search_button = A(T("Select Existing Location"),
 
897
                          _style="cursor:pointer; cursor:hand",
 
898
                          _id="gis_location_search-btn")
 
899
 
 
900
        tabs = DIV(SPAN(add_button, _id="gis_loc_add_tab", _class="tab_here"),
 
901
                   SPAN(search_button, _id="gis_loc_search_tab",
 
902
                        _class="tab_last"),
 
903
                   _class="tabs")
 
904
        tab_rows = TR(tabs)
 
905
 
 
906
        # L0 selector
 
907
        level = "L0"
 
908
        L0_rows = ""
 
909
        if value:
 
910
            # Read-only
 
911
            widget = INPUT(value=default[level],
 
912
                           _id="gis_location_%s" % level,
 
913
                           _disabled="disabled")
 
914
        elif len(countries) == 1:
 
915
            # Hard-coded country
 
916
            L0_rows = INPUT(_id="gis_location_%s" % level, _class="hidden")
 
917
        else:
 
918
            attr_dropdown = OptionsWidget._attributes(field,
 
919
                                                      dict(_type = "int",
 
920
                                                           value = ""),
 
921
                                                      **attributes)
 
922
            requires = IS_ONE_OF(db, "gis_location.id", repr_select,
 
923
                                 filterby = "level",
 
924
                                 filter_opts = (level,),
 
925
                                 orderby = "gis_location.name",
 
926
                                 sort = True,
 
927
                                 zero = "%s..." % select_location)
 
928
            if countries:
 
929
                # Use the list of countries from deployment_settings instead of from db
 
930
                options = []
 
931
                for country in countries:
 
932
                    options.append((countries[country].id,
 
933
                                    countries[country].name))
 
934
            else:
 
935
                # Prepopulate top-level dropdown from db
 
936
                if hasattr(requires, "options"):
 
937
                    options = requires.options()
 
938
                    if options.__len__() == 2:
 
939
                        # If there is only a single country available then pre-select it
 
940
                        options = [options[1]]
 
941
                else:
 
942
                    raise SyntaxError, "widget cannot determine options of %s" \
 
943
                                       % field
 
944
            opts = [OPTION(v, _value=k) for (k, v) in options]
 
945
            attr_dropdown["_id"] = "gis_location_%s" % level
 
946
            # Need to blank the name to prevent it from appearing in form.vars & requiring validation
 
947
            attr_dropdown["_name"] = ""
 
948
            widget = SELECT(*opts, **attr_dropdown)
 
949
        if not L0_rows:
 
950
            label = LABEL("%s:" % location_hierarchy[level])
 
951
            L0_rows = DIV(TR(TD(label),
 
952
                             _id="gis_location_%s_label__row" % level),
 
953
                          TR(TD(widget),
 
954
                             _id="gis_location_%s__row" % level))
 
955
 
 
956
        name_label = T("Building Name")
 
957
        street_label = T("Street Address")
 
958
        postcode_label = T("Postcode")
 
959
        lat_label = T("Latitude")
 
960
        lon_label = T("Longitude")
 
961
        autocomplete_help = T("Enter some characters to bring up a list of possible matches.")
 
962
        new_help = T("If not found, you can have a new location created.")
 
963
        def ac_help_widget(level):
 
964
            return DIV( _class="tooltip",
 
965
                        _title="%s|%s|%s" % (location_hierarchy[level], autocomplete_help, new_help))
 
966
 
 
967
        hidden = ""
 
968
        throbber = "/%s/static/img/ajax-loader.gif" % request.application
 
969
        Lx_rows = DIV()
 
970
        levels = ["L5", "L4", "L3", "L2", "L1"]
 
971
        if value:
 
972
            # Read-only
 
973
            name_widget = INPUT(value=represent,
 
974
                                _id="gis_location_name",
 
975
                                _disabled="disabled")
 
976
            street_widget = TEXTAREA(value=addr_street,
 
977
                                     _id="gis_location_street",
 
978
                                     _disabled="disabled")
 
979
            postcode_widget = INPUT(value=postcode,
 
980
                                    _id="gis_location_postcode",
 
981
                                    _disabled="disabled")
 
982
            lat_widget = INPUT(value=lat,
 
983
                               _id="gis_location_lat",
 
984
                               _disabled="disabled")
 
985
            lon_widget = INPUT(value=lon,
 
986
                               _id="gis_location_lon",
 
987
                               _disabled="disabled")
 
988
            for level in levels:
 
989
                if level not in location_hierarchy:
 
990
                    # Skip levels not in hierarchy
 
991
                    continue
 
992
                label = LABEL("%s:" % location_hierarchy[level])
 
993
                row = TR(TD(label),
 
994
                         _id="gis_location_%s_label__row" % level)
 
995
                Lx_rows.append(row)
 
996
                widget = INPUT(value=default[level],
 
997
                               _id="gis_location_%s" % level,
 
998
                               _disabled="disabled")
 
999
                row = TR(TD(widget),
 
1000
                         _id="gis_location_%s__row" % level)
 
1001
                Lx_rows.append(row)
 
1002
                
 
1003
        else:
 
1004
            name_widget = INPUT(_id="gis_location_name")
 
1005
            street_widget = TEXTAREA(_id="gis_location_street")
 
1006
            postcode_widget = INPUT(_id="gis_location_postcode")
 
1007
            lat_widget = INPUT(_id="gis_location_lat")
 
1008
            lon_widget = INPUT(_id="gis_location_lon")
 
1009
            for level in levels:
 
1010
                if level not in location_hierarchy:
 
1011
                    # Skip levels not in hierarchy
 
1012
                    continue
 
1013
                if len(countries) == 1:
 
1014
                    # Q: Make the L1 selector into a dropdown?
 
1015
                    # deployment_setting? Based on flag in the region table to say that we have this populated?
 
1016
                    pass
 
1017
                elif level in ["L5", "L4", "L3"]:
 
1018
                    # Have the levels ready, but don't display them until the Country Selector has run
 
1019
                    # so that we know which location hierarchy levels/labels to use
 
1020
                    hidden = "hidden"
 
1021
                label = LABEL("%s:" % location_hierarchy[level])
 
1022
                row = TR(TD(label),
 
1023
                         _class=hidden,
 
1024
                         _id="gis_location_%s_label__row" % level)
 
1025
                Lx_rows.append(row)
 
1026
                widget = DIV(INPUT(_id="gis_location_%s" % level,
 
1027
                                      _class="hidden"),
 
1028
                                INPUT(_id="gis_location_%s_ac" % level),
 
1029
                                IMG(_src=throbber,
 
1030
                                    _height=32, _width=32,
 
1031
                                    _id="gis_location_%s_throbber" % level,
 
1032
                                _class="throbber hidden"))
 
1033
                row = TR(TD(widget),
 
1034
                         TD(ac_help_widget(level)),
 
1035
                         _class=hidden,
 
1036
                         _id="gis_location_%s__row" % level)
 
1037
                Lx_rows.append(row)
 
1038
                
 
1039
        name_rows = DIV(TR(LABEL("%s:" % name_label),
 
1040
                           _id="gis_location_name_label__row"),
 
1041
                        TR(name_widget,
 
1042
                           _id="gis_location_name__row"))
 
1043
        street_rows = DIV(TR(LABEL("%s:" % street_label),
 
1044
                             _id="gis_location_street_label__row"),
 
1045
                          TR(street_widget,
 
1046
                             _id="gis_location_street__row"))
 
1047
        postcode_rows = DIV(TR(LABEL("%s:" % postcode_label),
 
1048
                               _id="gis_location_postcode_label__row"),
 
1049
                            TR(postcode_widget,
 
1050
                               _id="gis_location_postcode__row"))
 
1051
 
 
1052
        latlon_help = locations.lat.comment
 
1053
        converter_button = locations.lon.comment
 
1054
        converter_button = ""
 
1055
        latlon_rows = DIV(TR(LABEL("%s:" % lat_label),
 
1056
                               _id="gis_location_lat_label__row"),
 
1057
                          TR(TD(lat_widget), TD(latlon_help),
 
1058
                               _id="gis_location_lat__row"),
 
1059
                          TR(LABEL("%s:" % lon_label),
 
1060
                               _id="gis_location_lon_label__row"),
 
1061
                          TR(TD(lon_widget), TD(converter_button),
 
1062
                               _id="gis_location_lon__row"))
 
1063
 
 
1064
        if map_selector:
 
1065
            if value:
 
1066
                map_button = A(T("View on Map"),
 
1067
                               _style="cursor:pointer; cursor:hand",
 
1068
                               _id="gis_location_map-btn")
 
1069
            else:
 
1070
                map_button = A(T("Place on Map"),
 
1071
                               _style="cursor:pointer; cursor:hand",
 
1072
                               _id="gis_location_map-btn")
 
1073
        else:
 
1074
            map_button = ""
 
1075
 
 
1076
        autocomplete = DIV(INPUT(_id="gis_location_autocomplete"),
 
1077
                           IMG(_src=throbber,
 
1078
                               _height=32, _width=32,
 
1079
                               _id="gis_location_autocomplete_throbber",
 
1080
                               _class="throbber hidden"),
 
1081
                            _id="gis_location_autocomplete_div",
 
1082
                           _class="hidden")
 
1083
 
 
1084
        # Settings to be read by static/scripts/S3/s3.locationselector.widget.js
 
1085
        js_location_selector = """
 
1086
    S3.gis.uuid = '%s';
 
1087
    S3.gis.name = '%s';
 
1088
    S3.gis.addr_street = '%s';
 
1089
    S3.gis.postcode = '%s';
 
1090
    S3.gis.lat = '%s';
 
1091
    S3.gis.lon = '%s';
 
1092
    var s3_gis_location_id = '%s';
 
1093
    var s3_gis_url = '%s';
 
1094
    var s3_navigate_away_confirm = %s;
 
1095
    """ % (
 
1096
            uuid,
 
1097
            represent,
 
1098
            addr_street_encoded,
 
1099
            postcode,
 
1100
            lat,
 
1101
            lon,
 
1102
            attr["_id"],                    # Name of the real location field
 
1103
            URL(r=request, c="gis", f="location"),
 
1104
            navigate_away_confirm           # Currently unused
 
1105
          )
 
1106
        
 
1107
        # The overall layout of the components
 
1108
        return TAG[""](
 
1109
                        TR(INPUT(**attr)),  # Real input, which is hidden
 
1110
                        divider,
 
1111
                        #section_label,
 
1112
                        tab_rows,          # When ready
 
1113
                        L0_rows,
 
1114
                        TR(map_popup),
 
1115
                        name_rows,
 
1116
                        street_rows,
 
1117
                        postcode_rows,
 
1118
                        Lx_rows,
 
1119
                        TR(map_button),
 
1120
                        latlon_rows,
 
1121
                        divider,
 
1122
                        SCRIPT(js_location_selector)
 
1123
                      )
 
1124
        
 
1125
 
 
1126
# -----------------------------------------------------------------------------
693
1127
class S3LocationSelectorWidget(FormWidget):
694
1128
 
695
1129
    """
705
1139
        @see: http://eden.sahanafoundation.org/wiki/BluePrintGISLocationSelector
706
1140
 
707
1141
        @ToDo: Need to allow Projects/Documents to be linked to a generic Lx Location.
 
1142
        @ToDo: Make the Search Locations' Dropdowns into Search Filters
 
1143
        @ToDo: Don't do separate AJAX calls to populate the Hierarchy levels when a location is found - simply get in the 1 pull
708
1144
    """
709
1145
 
710
1146
    def __init__(self,
770
1206
        max_hierarchy = deployment_settings.get_gis_max_hierarchy()
771
1207
        # Is full hierarchy mandatory?
772
1208
        #strict = deployment_settings.get_gis_strict_hierarchy()
773
 
        # @ToDo: Do soem client-side validation based on this flag
 
1209
        # @ToDo: Do some client-side validation based on this flag
774
1210
 
775
1211
        # Navigate Away Confirm?
776
1212
        if deployment_settings.get_ui_navigate_away_confirm():
825
1261
            parent = this_location.parent
826
1262
            path = this_location.path
827
1263
 
828
 
            # Get ids of ancestors at each level.
 
1264
            # Populate default with IDs of ancestors at each level
829
1265
            gis.get_parent_per_level(default, value, feature=this_location)
830
1266
 
831
1267
            # Provide the representation for the current/default Value
1420
1856
 
1421
1857
 
1422
1858
# -----------------------------------------------------------------------------
1423
 
class S3StreetAddressWidget(FormWidget):
1424
 
 
1425
 
    """
1426
 
        @ToDo:
1427
 
        Renders a gis_location SELECT suitable for use as a Street Address:
1428
 
            - a set of INPUT fields ordered in 'normal' order
1429
 
            - street address field triggers a geocoder lookup (direct to Google @ToDo: configurable which back-end to use)
1430
 
            - if succesful then the rest of the hierarchy is populated
1431
 
            - Lx fields act as optional Autocompletes
1432
 
            - any levels of hierarchy which weren't selected get created along with the main record through a single AJAX request
1433
 
            Q: How do we avoid duplicates with the auto-population of the hierarchy from the geocoder? (Check for same name => Update in this processing controller?)
1434
 
            - map opens up to the geocoded location & can be fine-tuned from there
1435
 
 
1436
 
        @author: Fran Boon (fran@aidiq.com)
1437
 
 
1438
 
        @see: http://eden.sahanafoundation.org/wiki/BluePrintGISLocationSelector
1439
 
    """
1440
 
 
1441
 
    def __init__(self,
1442
 
                 db,
1443
 
                 gis,
1444
 
                 deployment_settings,
1445
 
                 request,
1446
 
                 response,
1447
 
                 T,
1448
 
                 #level=None        # @ToDo: Support forcing which level of the hierarchy is expected to be entered for this instance of the field
1449
 
                 ):
1450
 
 
1451
 
        self.db = db
1452
 
        self.gis = gis
1453
 
        self.deployment_settings = deployment_settings
1454
 
        self.request = request
1455
 
        self.response = response
1456
 
        self.T = T
1457
 
 
1458
 
    def __call__(self, field, value, **attributes):
1459
 
 
1460
 
        db = self.db
1461
 
        gis = self.gis
1462
 
        deployment_settings = self.deployment_settings
1463
 
        request = self.request
1464
 
        response = self.response
1465
 
        T = self.T
1466
 
 
1467
 
        # shortcut
1468
 
        locations = db.gis_location
1469
 
 
1470
 
        countries = response.s3.gis.countries  # Also needed by location_represent hence want to keep in model, so useful not to repeat
1471
 
        # Should we use a Map-based selector?
1472
 
        map_selector = deployment_settings.get_gis_map_selector()
1473
 
        # Which Levels do we have in our hierarchy & what are their initial Labels?
1474
 
        location_hierarchy = deployment_settings.get_gis_locations_hierarchy()
1475
 
        # What is the maximum level of hierarchy?
1476
 
        max_hierarchy = deployment_settings.get_gis_max_hierarchy()
1477
 
 
1478
 
        # Navigate Away Confirm?
1479
 
        if deployment_settings.get_ui_navigate_away_confirm():
1480
 
            navigate_away_confirm = "true"
1481
 
        else:
1482
 
            navigate_away_confirm = "false"
1483
 
        
1484
 
        # Main Input
1485
 
        default = dict(
1486
 
                        _type = "text",
1487
 
                        value = (value != None and str(value)) or "",
1488
 
                        L0 = None,
1489
 
                        L1 = None,
1490
 
                        L2 = None,
1491
 
                        L3 = None,
1492
 
                        L4 = None,
1493
 
                        L5 = None
1494
 
                    )
1495
 
        attr = StringWidget._attributes(field, default, **attributes)
1496
 
        # Hide the real field
1497
 
        attr["_class"] = "hidden"
1498
 
 
1499
 
        map_popup = ""
1500
 
 
1501
 
        if value:
1502
 
            # Read current record
1503
 
            this_location = db(locations.id == value).select(locations.uuid,
1504
 
                                                             locations.name,
1505
 
                                                             locations.level,
1506
 
                                                             locations.lat,
1507
 
                                                             locations.lon,
1508
 
                                                             locations.addr_street,
1509
 
                                                             locations.addr_postcode,
1510
 
                                                             locations.parent,
1511
 
                                                             locations.path,
1512
 
                                                             limitby=(0, 1)).first()
1513
 
            # @ToDo Is it possible that value does not point to an existing
1514
 
            # location?
1515
 
            uuid = this_location.uuid
1516
 
            level = this_location.level
1517
 
            default[level] = value
1518
 
            lat = this_location.lat
1519
 
            lon = this_location.lon
1520
 
            addr_street = this_location.addr_street or ""
1521
 
            addr_street_encoded = ""
1522
 
            if addr_street:
1523
 
                addr_street_encoded = addr_street.replace("\r\n",
1524
 
                                                          "%0d").replace("\r",
1525
 
                                                                         "%0d").replace("\n",
1526
 
                                                                                        "%0d")
1527
 
            postcode = this_location.addr_postcode
1528
 
            parent = this_location.parent
1529
 
            path = this_location.path
1530
 
 
1531
 
            # Get ids of ancestors at each level.
1532
 
            gis.get_parent_per_level(default, value, feature=this_location)
1533
 
 
1534
 
            # Provide the representation for the current/default Value
1535
 
            #text = str(field.represent(default["value"]))
1536
 
            #if "<" in text:
1537
 
            #    # Strip Markup
1538
 
            #    try:
1539
 
            #        markup = etree.XML(text)
1540
 
            #        text = markup.xpath(".//text()")
1541
 
            #        if text:
1542
 
            #            text = " ".join(text)
1543
 
            #        else:
1544
 
            #            text = ""
1545
 
            #    except etree.XMLSyntaxError:
1546
 
            #        pass
1547
 
            #represent = text
1548
 
            if level:
1549
 
                # If within the locations hierarchy then don't populate the visible name box
1550
 
                represent = ""
1551
 
            else:
1552
 
                represent = this_location.name
1553
 
 
1554
 
            if map_selector:
1555
 
                config = gis.get_config()
1556
 
                zoom = config.zoom
1557
 
                if lat is None or lon is None:
1558
 
                    map_lat = config.lat
1559
 
                    map_lon = config.lon
1560
 
                else:
1561
 
                    map_lat = lat
1562
 
                    map_lon = lon
1563
 
 
1564
 
                layername = T("Location")
1565
 
                popup_label = ""
1566
 
                layer = gis.get_feature_layer("gis",
1567
 
                                              "location",
1568
 
                                              layername,
1569
 
                                              popup_label,
1570
 
                                              id=value)
1571
 
                if layer:
1572
 
                    feature_queries = [layer]
1573
 
                else:
1574
 
                    feature_queries = []
1575
 
                map_popup = gis.show_map(lat = map_lat,
1576
 
                                         lon = map_lon,
1577
 
                                         # Same as a single zoom on a cluster
1578
 
                                         zoom = zoom + 2,
1579
 
                                         feature_queries = feature_queries,
1580
 
                                         add_feature = True,
1581
 
                                         add_feature_active = False,
1582
 
                                         toolbar = True,
1583
 
                                         collapsed = True,
1584
 
                                         search = True,
1585
 
                                         window = True,
1586
 
                                         window_hide = True)
1587
 
 
1588
 
        else:
1589
 
            # No default value
1590
 
            uuid = ""
1591
 
            represent = ""
1592
 
            level = None
1593
 
            lat = ""
1594
 
            lon = ""
1595
 
            addr_street = ""
1596
 
            addr_street_encoded = ""
1597
 
            postcode = ""
1598
 
            if map_selector:
1599
 
                map_popup = gis.show_map(
1600
 
                                         add_feature = True,
1601
 
                                         add_feature_active = True,    # http://trac.osgeo.org/openlayers/ticket/3179
1602
 
                                         toolbar = True,
1603
 
                                         collapsed = True,
1604
 
                                         search = True,
1605
 
                                         window = True,
1606
 
                                         window_hide = True)
1607
 
 
1608
 
        # Settings to insert into static/scripts/S3/s3.locationselector.widget.js
1609
 
        location_id = attr["_id"]
1610
 
        url = URL(r=request, c="gis", f="location")
1611
 
 
1612
 
        # Localised strings
1613
 
        loading_locations = T("Loading Locations")
1614
 
        select_location = T("Select a location")
1615
 
        degrees_validation_error = T("Degrees must be a number between -180 and 180")
1616
 
        minutes_validation_error = T("Minutes must be a number greater than 0 and less than 60")
1617
 
        seconds_validation_error = T("Seconds must be a number greater than 0 and less than 60")
1618
 
        no_calculations_error = T("No calculations made")
1619
 
        fill_lat = T("Fill in Latitude")
1620
 
        fill_lon = T("Fill in Longitude")
1621
 
 
1622
 
        # Settings to be read by static/scripts/S3/s3.locationselector.widget.js
1623
 
        js_location_selector = """
1624
 
    var s3_gis_location_id = '%s';
1625
 
    var s3_gis_maxlevel = '%s';
1626
 
    var s3_gis_loading_locations = '<option value="">%s...</option>';
1627
 
    var s3_gis_select_location = '<option value="" selected>%s...</option>';
1628
 
    var s3_gis_url = '%s';
1629
 
    S3.gis.uuid = '%s';
1630
 
    S3.gis.name = '%s';
1631
 
    S3.gis.addr_street = '%s';
1632
 
    S3.gis.postcode = '%s';
1633
 
    S3.gis.lat = '%s';
1634
 
    S3.gis.lon = '%s';
1635
 
    var s3_gis_degrees_validation_error = '%s';
1636
 
    var s3_gis_minutes_validation_error = '%s';
1637
 
    var s3_gis_seconds_validation_error = '%s';
1638
 
    var s3_gis_no_calculations_error = '%s';
1639
 
    var s3_gis_fill_lat = '%s';
1640
 
    var s3_gis_fill_lon = '%s';
1641
 
    var s3_navigate_away_confirm = %s;
1642
 
    """ % (location_id,
1643
 
           max_hierarchy[1:],
1644
 
           loading_locations,
1645
 
           select_location,
1646
 
           url,
1647
 
           uuid,
1648
 
           represent,
1649
 
           addr_street_encoded,
1650
 
           postcode,
1651
 
           lat or "",
1652
 
           lon or "",
1653
 
           degrees_validation_error,
1654
 
           minutes_validation_error,
1655
 
           seconds_validation_error,
1656
 
           no_calculations_error,
1657
 
           fill_lat,
1658
 
           fill_lon,
1659
 
           navigate_away_confirm
1660
 
          )
1661
 
 
1662
 
        # Labels
1663
 
        name_label = DIV(LABEL("%s:" % T("Building Name")),
1664
 
                         #SPAN("*", _class="req"),
1665
 
                         _id="gis_location_name_label", _class=details_hidden)
1666
 
        street_label = TR(LABEL("%s:" % T("Street Address")),
1667
 
                          _id="gis_location_addr_street_label",
1668
 
                          _class=details_hidden)
1669
 
        postcode_label = TR(LABEL("%s:" % T("Postcode")),
1670
 
                            _id="gis_location_postcode_label",
1671
 
                            _class=details_hidden)
1672
 
        lat_label = TR(LABEL("%s:" % T("Latitude")),
1673
 
                       _id="gis_location_lat_label",
1674
 
                       _class="hidden")
1675
 
        lon_label = TR(LABEL("%s:" % T("Longitude")),
1676
 
                       _id="gis_location_lon_label",
1677
 
                       _class="hidden")
1678
 
 
1679
 
        # Form Fields
1680
 
        street_widget = TEXTAREA(addr_street, _id="gis_location_addr_street")
1681
 
        postcode_widget = INPUT(_id="gis_location_postcode", _value=postcode)
1682
 
        lat_widget = INPUT(_id="gis_location_lat", _value=lat)
1683
 
        lon_widget = INPUT(_id="gis_location_lon", _value=lon)
1684
 
 
1685
 
        autocomplete = DIV(LABEL("%s:" % T("Enter some characters to bring up a list of possible matches")),
1686
 
                           BR(),
1687
 
                           INPUT(_id="gis_location_autocomplete"),
1688
 
                           _id="gis_location_autocomplete_div",
1689
 
                           _class="hidden")
1690
 
 
1691
 
        geolocate_button = A(T("Use Current Location"),
1692
 
                             _style="cursor:pointer; cursor:hand",
1693
 
                             _id="gis_location_geolocate-btn",
1694
 
                             _class=details_hidden)
1695
 
 
1696
 
        if map_selector:
1697
 
            map_button = A(T("Place on Map"),
1698
 
                           _style="cursor:pointer; cursor:hand",
1699
 
                           _id="gis_location_map-btn",
1700
 
                           _class=details_hidden)
1701
 
        else:
1702
 
            map_button = ""
1703
 
 
1704
 
        geocoder_button = A(T("Lookup Address"),
1705
 
                            _style="cursor:pointer; cursor:hand",
1706
 
                            _id="gis_location_geocoder-btn")
1707
 
 
1708
 
        latlon_help = locations.lat.comment
1709
 
        converter_button = locations.lon.comment
1710
 
 
1711
 
        advanced_checkbox = DIV("%s:" % T("Advanced"),
1712
 
                                INPUT(_type="checkbox",
1713
 
                                      _id="gis_location_advanced_checkbox",
1714
 
                                      value=""),
1715
 
                                _id="gis_location_advanced_div",
1716
 
                                _class=details_hidden)
1717
 
 
1718
 
        # @ToDo: Replace with simple alternate input forms: Radio button defaults to decimal degrees (real inputs), but can select GPS or DDMMSS
1719
 
        gps_converter_popup = DIV(
1720
 
            DIV(T("Coordinate Conversion"), _class="x-window-header"),
1721
 
            DIV(
1722
 
                DIV(
1723
 
                    P(
1724
 
                        TABLE(
1725
 
                            TR(
1726
 
                                TD(
1727
 
                                    B("%s:" % T("Enter a GPS Coordinate")),
1728
 
                                    INPUT(_type="text", _size="3", _id="gps_deg"), "deg",
1729
 
                                    INPUT(_type="text", _size="6", _id="gps_min"), "min",
1730
 
                                ),
1731
 
                            ),
1732
 
                            TR(
1733
 
                                TD(
1734
 
                                    B("%s:" % T("Decimal Degrees")),
1735
 
                                    INPUT(_type="text", _size="8", _id="gps_dec"),
1736
 
                                ),
1737
 
                            ),
1738
 
                            TR(
1739
 
                                TD(
1740
 
                                    INPUT(_type="button", _value=T("Calculate"), _onclick="s3_gis_convertGps()"),
1741
 
                                    INPUT(_type="reset", _value=T("Reset"), _onclick="s3_gis_convertGps()"),
1742
 
                                ),
1743
 
                            ),
1744
 
                        _border="0", _cellpadding="2", _width="400"
1745
 
                        ),
1746
 
                    ),
1747
 
                _class="x-tab", _title=T("in GPS format")
1748
 
                ),
1749
 
 
1750
 
                DIV(
1751
 
                    P(
1752
 
                        TABLE(
1753
 
                            TR(
1754
 
                                TD(
1755
 
                                    B("%s:" % T("Enter Coordinates")),
1756
 
                                    INPUT(_type="text", _size="3", _id="DDMMSS_deg"), "Deg",
1757
 
                                    INPUT(_type="text", _size="2", _id="DDMMSS_min"), "Min",
1758
 
                                    INPUT(_type="text", _size="2", _id="DDMMSS_sec"), "Sec",
1759
 
                                ),
1760
 
                            ),
1761
 
                            TR(
1762
 
                                TD(
1763
 
                                    B("%s:" % T("Decimal Degrees")),
1764
 
                                    INPUT(_type="text", _size="8", _id="DDMMSS_dec"),
1765
 
                                ),
1766
 
                            ),
1767
 
                            TR(
1768
 
                                TD(
1769
 
                                    INPUT(_type="button", _value=T("Calculate"), _onclick="s3_gis_convertDD()"),
1770
 
                                    INPUT(_type="reset", _value=T("Reset")),
1771
 
                                ),
1772
 
                            ),
1773
 
                        _border="0", _cellpadding="2", _width="400"
1774
 
                        ),
1775
 
                    ),
1776
 
                _class="x-tab", _title=T("in Deg Min Sec format")
1777
 
                ),
1778
 
            _id="gis-convert-tabs"),
1779
 
        _id="gis-convert-win", _class="x-hidden")
1780
 
 
1781
 
        # Rows
1782
 
        #if represent:
1783
 
        #    # We have a specific location to show
1784
 
        #    name_rows = DIV(TR(name_label),
1785
 
        #                    TR(INPUT(_id="gis_location_name", _value=represent)))
1786
 
        #else:
1787
 
        name_rows = DIV(TR(name_label),
1788
 
                        TR(INPUT(_id="gis_location_name", _value=represent,
1789
 
                                 _class=details_hidden),
1790
 
                           details_hide_button))
1791
 
        street_rows = DIV(street_label,
1792
 
                          # @ToDo: Enable Geocoder here when ready
1793
 
                          #TR(street_widget, geocoder_button, _id="gis_location_addr_street_row", _class="hidden"))
1794
 
                          TR(street_widget, _id="gis_location_addr_street_row",
1795
 
                             _class=details_hidden))
1796
 
        postcode_rows = DIV(postcode_label,
1797
 
                            TR(postcode_widget,
1798
 
                               _id="gis_location_postcode_row",
1799
 
                               _class=details_hidden))
1800
 
        lat_rows = DIV(lat_label,
1801
 
                       TR(lat_widget, latlon_help, _id="gis_location_lat_row",
1802
 
                          _class="hidden"))
1803
 
        lon_rows = DIV(lon_label,
1804
 
                       TR(lon_widget, converter_button,
1805
 
                          _id="gis_location_lon_row", _class="hidden"))
1806
 
        divider = TR(TD(_class="subheading"))
1807
 
 
1808
 
        # The overall layout of the components
1809
 
        return TAG[""](
1810
 
                        #divider,           # This is in the widget, so underneath the label :/ Add in JS? 'Sections'?
1811
 
                        TR(INPUT(**attr)),  # Real input, which is hidden
1812
 
                        TR(gps_converter_popup),
1813
 
                        TR(map_popup),
1814
 
                        name_rows,
1815
 
                        street_rows,
1816
 
                        postcode_rows,
1817
 
                        # @ToDo: Enable GeoLocate here when ready
1818
 
                        #TR(geolocate_button),
1819
 
                        TR(map_button),
1820
 
                        TR(advanced_checkbox),
1821
 
                        lat_rows,
1822
 
                        lon_rows,
1823
 
                        divider,
1824
 
                        SCRIPT(js_location_selector)
1825
 
                      )
1826
 
 
1827
 
 
1828
 
# -----------------------------------------------------------------------------
1829
1859
class S3CheckboxesWidget(OptionsWidget):
1830
1860
 
1831
1861
    """