예제 #1
0
class ICategoryMappingRowSchema(Interface):
    local_category_id = schema.TextLine(title=_(u"Local category id"))
    global_category_id = schema.Choice(
        title=_(u"Global category"),
        vocabulary="plonemeeting.portal.vocabularies.global_categories",
        required=True,
    )
예제 #2
0
class ImportMeetingForm(AutoExtensibleForm, Form):
    """
    """

    schema = IImportMeetingForm
    ignoreContext = True

    label = _(u"Meeting import form")
    description = _(u"Choose the meeting you want to import in the portal.")

    @button.buttonAndHandler(_(u"Import"))
    def handle_apply(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        institution = self.context
        meeting_uid = data.get("meeting")
        _sync_meeting(institution, meeting_uid, self.request)

    @button.buttonAndHandler(_(u"Cancel"))
    def handle_cancel(self, action):
        """
        """
        self.request.response.redirect(self.context.absolute_url())
예제 #3
0
def handle_institution_creation(obj, event):
    current_lang = api.portal.get_default_language()[:2]
    institution_title = obj.title

    # Configure manager group & local permissions
    group_id = format_institution_managers_group_id(obj)
    group_title = "{0} Institution Managers".format(institution_title)
    api.group.create(groupname=group_id, title=group_title)
    obj.manage_setLocalRoles(group_id, ["Institution Manager", "Contributor"])

    # Create meetings faceted folder
    meetings = create_faceted_folder(
        obj, translate(_(u"Meetings"), target_language=current_lang))
    alsoProvides(meetings, IMeetingsFolder)
    IFacetedLayout(meetings).update_layout("faceted-preview-meeting")

    # Create items faceted folder
    items = create_faceted_folder(
        obj, translate(_(u"Decisions"), target_language=current_lang))
    alsoProvides(items, IItemsFolder)
    IFacetedLayout(items).update_layout("faceted-preview-meeting-items")

    request = getRequest()
    if request:  # Request can be `None` during test setup
        request.response.redirect(obj.absolute_url())
예제 #4
0
def post_install(context):
    """Post install script"""
    portal = api.portal.get()
    current_lang = api.portal.get_default_language()[:2]
    faceted_config = "/faceted/config/items.xml"

    if "config" in portal.objectIds():
        return

    remove_left_portlets()
    remove_right_portlets()
    cleanup_contents()

    # Create global config folder
    config_folder = api.content.create(
        container=portal,
        type="Folder",
        title=translate(_(u"Configuration folder"),
                        target_language=current_lang),
        id=CONFIG_FOLDER_ID,
    )
    config_folder.exclude_from_nav = True

    # Create global faceted folder
    faceted = create_faceted_folder(
        config_folder,
        translate(_(u"Faceted"), target_language=current_lang),
        id=FACETED_FOLDER_ID,
    )
    subtyper = faceted.restrictedTraverse("@@faceted_subtyper")
    subtyper.enable()
    with open(os.path.dirname(__file__) + faceted_config,
              "rb") as faceted_config:
        faceted.unrestrictedTraverse("@@faceted_exportimport").import_xml(
            import_file=faceted_config)
예제 #5
0
class ItemsSortWidget(Widget):
    """ Sort items with custom (multiple) sort orders
    """

    widget_type = "items_sort"
    widget_label = _("Items sort order")
    groups = (DefaultSchemata,)

    index = ViewPageTemplateFile("sort.pt")

    def query(self, form):
        """ Sort items by meeting date (desc) and by item number (asc)
        """
        # XXX avoid double sort_on when we selected a meeting
        # this is not necessary and it some case, produce weird results
        if "seance" in form:
            query = {
                "sort_on": ["sortable_number"],
                "sort_order": ["ascending"],
            }
        else:
            query = {
                "sort_on": ["linkedMeetingDate", "sortable_number"],
                "sort_order": ["descending", "ascending"],
            }
        return query
예제 #6
0
class IImportMeetingForm(Interface):
    """
    """

    meeting = schema.Choice(
        title=_(u"Meeting"),
        vocabulary="plonemeeting.portal.vocabularies.remote_meetings",
        required=True,
    )
예제 #7
0
class IMeeting(model.Schema):
    """ Marker interface and Dexterity Python Schema for Meeting
    """

    title = schema.TextLine(title=plone_(u"Title"), required=True, readonly=True)

    plonemeeting_uid = schema.TextLine(
        title=_(u"UID Plonemeeting"), required=True, readonly=True,
    )

    form.write_permission(date_time=ManagePortal)
    date_time = schema.Datetime(title=plone_(u"Date"), required=True, readonly=False,)

    custom_info = RichText(title=_(u"Custom info"), required=False)

    plonemeeting_last_modified = schema.Datetime(
        title=_(u"Last modification in iA.Delib"), required=True, readonly=True
    )
예제 #8
0
class IItem(model.Schema):
    """ Marker interface and Dexterity Python Schema for Item
    """

    dexteritytextindexer.searchable("formatted_title")
    formatted_title = RichText(title=plone_(u"Title"),
                               required=False,
                               readonly=True)

    number = schema.TextLine(title=_(u"Item number"),
                             required=True,
                             readonly=True)

    sortable_number = schema.Int(title=_(u"Sortable Item number"),
                                 required=True,
                                 readonly=True)

    plonemeeting_uid = schema.TextLine(title=_(u"UID Plonemeeting"),
                                       required=True,
                                       readonly=True)

    representatives_in_charge = schema.List(
        value_type=schema.Choice(
            vocabulary="plonemeeting.portal.vocabularies.representatives"),
        title=_(u"Representative group in charge"),
        required=False,
        readonly=True,
    )

    additional_data = RichText(title=_(u"Additional data"),
                               required=False,
                               readonly=True)

    dexteritytextindexer.searchable("decision")
    decision = RichText(title=_(u"Decision"), required=False, readonly=True)

    category = schema.Choice(
        vocabulary="plonemeeting.portal.vocabularies.global_categories",
        title=_(u"Category"),
        required=False,
        readonly=True,
    )

    custom_info = RichText(title=_(u"Custom info"), required=False)

    plonemeeting_last_modified = schema.Datetime(
        title=_(u"Last modification in iA.Delib"),
        required=True,
        readonly=True)
예제 #9
0
def format_meeting_date_and_state(date,
                                  state_id,
                                  format="%d %B %Y (%H:%M)",
                                  lang=None):
    """
    Format the meeting date while managing translations of months and weekdays
    :param date: Datetime reprensenting the meeting date
    :param format: format of the returning date. See strftime for directives.
    """
    MONTHS_IDS = {
        1: "month_jan",
        2: "month_feb",
        3: "month_mar",
        4: "month_apr",
        5: "month_may",
        6: "month_jun",
        7: "month_jul",
        8: "month_aug",
        9: "month_sep",
        10: "month_oct",
        11: "month_nov",
        12: "month_dec",
    }
    WEEKDAYS_IDS = {
        0: "weekday_mon",
        1: "weekday_tue",
        2: "weekday_wed",
        3: "weekday_thu",
        4: "weekday_fri",
        5: "weekday_sat",
        6: "weekday_sun",
    }
    format = format.replace("%B", "[month]").replace("%A", "[weekday]")
    date_str = safe_unicode(date.strftime(format))

    if not lang:
        lang = api.portal.get_tool("portal_languages").getDefaultLanguage()

    # in some cases month are not properly translated using sublocales
    lang = lang.split("-")[0]

    if u"[month]" in date_str:
        month = translate(MONTHS_IDS[date.month],
                          domain="plonelocales",
                          target_language=lang)
        date_str = date_str.replace("[month]", month)

    if u"[weekday]" in date_str:
        weekday = translate(WEEKDAYS_IDS[date.weekday()],
                            domain="plonelocales",
                            target_language=lang)
        date_str = date_str.replace(u"[weekday]", weekday)

    state = translate(_(state_id), target_language=lang)
    return "{0} — {1}".format(date_str, state)
예제 #10
0
def sync_meeting(institution, meeting_uid, force=False):
    """
    synchronizes a single meeting through ia.Delib web services (Rest/JSON)
    :param force: Should force reload items. Default False
    :param institution: current institution
    :param meeting_uid: the uid of the meeting to fetch from ia.Delib
    :return: the sync status, the new meeting's UID
    status may be an error or a short summary of what happened.
    UID may be none in case of error from the web service
    """
    url = get_api_url_for_meetings(institution, meeting_UID=meeting_uid)
    response = _call_delib_rest_api(url, institution)

    json_meeting = json.loads(response.text)
    if json_meeting.get("items_total") != 1:
        raise ValueError(
            _(u"Unexpected meeting count in webservice response !"))

    meeting = sync_meeting_data(institution, json_meeting.get("items")[0])
    url = get_api_url_for_meeting_items(institution, meeting_UID=meeting_uid)
    response = _call_delib_rest_api(url, institution)

    json_items = json.loads(response.text)
    results = sync_items_data(meeting, json_items, institution, force)

    status_msg = _(
        u"meeting_imported",
        default=u"Meeting imported !  "
        u"${created} created items, "
        u"${modified} modified items, "
        u"${deleted} deleted items.",
        mapping={
            u"created": results["created"],
            u"modified": results["modified"],
            u"deleted": results["deleted"],
        },
    )
    current_lang = api.portal.get_default_language()[:2]
    status = translate(status_msg, target_language=current_lang)
    return status, meeting.UID()
예제 #11
0
def _call_delib_rest_api(url, institution):  # pragma: no cover
    start_time = time.time()
    logger.info("REST API CALL TO {0}".format(url))
    response = requests.get(url,
                            auth=(institution.username, institution.password),
                            headers=API_HEADERS)

    if response.status_code != 200:
        raise ValueError(_(u"Webservice connection error !"))
    msg, seconds = end_time(start_time, "REST API CALL MADE IN ", True)
    if seconds > 1:
        logger.warning(msg)
    else:
        logger.info(msg)

    return response
예제 #12
0
class NavigationRootPathWidget(Widget):
    """ Filter on objects from current navigation root
    """

    widget_type = "navigation_root_path"
    widget_label = _("Navigation root path")
    groups = (DefaultSchemata,)

    index = ViewPageTemplateFile("root_path.pt")

    def query(self, form):
        """ Returns only objects from current navigation root
        """
        nav_root = api.portal.get_navigation_root(self.context)
        path = "/".join(nav_root.getPhysicalPath())
        query = {"path": {"query": path}}
        return query
예제 #13
0
    def __call__(self):
        # Don't redirect if user can edit institution
        # Don't use api.user.has_permission since the method breaks robot tests
        if api.user.get_permissions(
                obj=self.context).get("Modify portal content"):
            api.portal.show_message(
                _("You see this page because you have permissions to edit it. "
                  "Otherwise you would have been redirected to Meetings folder. "
                  "To see the Meetings view, click on Meetings folder."),
                request=self.request,
                type="info",
            )
            return super(InstitutionView, self).__call__()

        institution = api.portal.get_navigation_root(self.context)
        meeting_folder_brains = api.content.find(
            context=institution,
            object_provides=IMeetingsFolder.__identifier__)
        if not meeting_folder_brains:
            return super(InstitutionView, self).__call__()
        url = meeting_folder_brains[0].getURL()
        self.request.response.redirect(url)
        return ""
예제 #14
0
class InvalidColorParameters(ValidationError):
    """Exception for invalid url parameters"""

    __doc__ = _(
        u"Invalid color parameter, the value should be a correct hexadecimal color"
    )
예제 #15
0
# -*- coding: utf-8 -*-
from plonemeeting.portal.core import _

CONFIG_FOLDER_ID = "config"
FACETED_FOLDER_ID = "faceted"

CONTENTS_TO_CLEAN = ["Members", "events", "news"]

PLONEMEETING_API_MEETING_TYPE = "meeting"
PLONEMEETING_API_ITEM_TYPE = "item"

API_HEADERS = {"Content-type": "application/json", "Accept": "application/json"}

# keep those ids in translations files
REVIEW_STATES_IDS = [_("private"), _("in_project"), _("decision")]

DEFAULT_CATEGORY_IA_DELIB_FIELD = "category"
CATEGORY_IA_DELIB_FIELDS = (
    ("category", _("category")),
    ("classifier", _("classifier")),
)
예제 #16
0
class IInstitution(model.Schema):
    """ Marker interface and Dexterity Python Schema for Institution
    """

    plonemeeting_url = schema.URI(title=_(u"Plonemeeting URL"), required=False)

    username = schema.TextLine(title=_(u"Username"), required=False)

    password = schema.TextLine(title=_(u"Password"), required=False)

    meeting_config_id = schema.TextLine(title=_(u"Meeting config ID"),
                                        required=False)

    project_decision_disclaimer = RichText(
        title=_(u"Project decision disclaimer"),
        required=False,
        defaultFactory=default_translator(
            _(u"default_in_project_disclaimer", default="")),
    )

    additional_meeting_query_string_for_list = schema.TextLine(
        title=_(u"Additional Meeting query string for list"),
        required=False,
        constraint=validate_url_parameters,
    )

    additional_published_items_query_string = schema.TextLine(
        title=_(u"Additional Published Items query string"),
        required=False,
        constraint=validate_url_parameters,
    )
    # Formatting fieldset
    model.fieldset(
        "formatting",
        label=_(u"Formatting"),
        fields=[
            "item_title_formatting_tal",
            "item_decision_formatting_tal",
            "item_additional_data_formatting_tal",
            "info_annex_formatting_tal",
        ],
    )

    item_title_formatting_tal = schema.TextLine(
        title=_(u"Item title formatting tal expression. "
                u"If empty the default title will be used"),
        required=False,
    )

    item_decision_formatting_tal = schema.TextLine(
        title=_(u"Item decision formatting tal expression"),
        required=True,
        default="python: json['decision']['data']",
    )

    item_additional_data_formatting_tal = schema.TextLine(
        title=_(u"Item additional data formatting tal expression"),
        required=False)

    info_annex_formatting_tal = schema.TextLine(
        title=_(u"Info annex formatting tal expression"), required=False)

    # Mapping fieldset
    model.fieldset(
        "mapping",
        label=_(u"Mapping"),
        fields=[
            "delib_category_field",
            "categories_mappings",
            "representatives_mappings",
        ],
    )
    delib_category_field = schema.Choice(
        title=_(u"iA.Delib field to use for category mapping"),
        vocabulary="plonemeeting.portal.vocabularies.delib_category_fields",
        required=True,
        default=DEFAULT_CATEGORY_IA_DELIB_FIELD)

    directives.widget("categories_mappings",
                      DataGridFieldFactory,
                      allow_reorder=True)
    categories_mappings = schema.List(
        title=_(u"Categories mappings"),
        value_type=DictRow(title=u"Category mapping",
                           schema=ICategoryMappingRowSchema),
        required=False,
    )

    directives.widget("representatives_mappings",
                      DataGridFieldFactory,
                      allow_reorder=True)
    representatives_mappings = schema.List(
        title=_(u"Representatives mappings"),
        value_type=DictRow(title=u"Representative mapping",
                           schema=IRepresentativeMappingRowSchema),
        required=False,
    )

    # Styling fieldset

    model.fieldset(
        "style",
        label=_(u"Styling"),
        fields=[
            "logo",
            "header_color",
            "nav_color",
            "nav_text_color",
            "links_color",
            "footer_color",
            "footer_text_color",
        ],
    )

    logo = NamedBlobImage(title=_(u"Logo"), required=False)

    directives.widget("header_color", ColorSelectFieldWidget)
    header_color = schema.TextLine(
        title=_("Header color"),
        required=True,
        default="#ffffff",
        constraint=validate_color_parameters,
    )

    directives.widget("nav_color", ColorSelectFieldWidget)
    nav_color = schema.TextLine(
        title=_("Navigation bar color"),
        required=True,
        default="#007bb1",  # Plone blue
        constraint=validate_color_parameters,
    )

    directives.widget("nav_text_color", ColorSelectFieldWidget)
    nav_text_color = schema.TextLine(
        title=_("Navigation bar text color"),
        required=True,
        default="#ffffff",
        constraint=validate_color_parameters,
    )

    directives.widget("links_color", ColorSelectFieldWidget)
    links_color = schema.TextLine(
        title=_("Links text color"),
        required=True,
        default="#007bb1",
        constraint=validate_color_parameters,
    )

    directives.widget("footer_color", ColorSelectFieldWidget)
    footer_color = schema.TextLine(
        title=_("Footer color"),
        required=True,
        default="#2e3133",
        constraint=validate_color_parameters,
    )

    directives.widget("footer_text_color", ColorSelectFieldWidget)
    footer_text_color = schema.TextLine(
        title=_("Footer text color"),
        required=True,
        default="#cccccc",
        constraint=validate_color_parameters,
    )
예제 #17
0
class IRepresentativeMappingRowSchema(Interface):
    representative_key = schema.TextLine(title=_(u"Representative key"))
    representative_value = schema.TextLine(title=_(u"Representative value"))
    representative_long_value = schema.TextLine(
        title=_(u"Representative long values"))
    active = schema.Bool(title=_(u"Active"), default=True)
예제 #18
0
 def validate(password, data):
     if len(password) < 8:
         return _(u"Passwords must be at least 8 characters in length.")
예제 #19
0
class InvalidUrlParameters(ValidationError):
    """Exception for invalid url parameters"""

    __doc__ = _(u"Invalid url parameters, the value should start with '&'")