diff --git a/Services/Style/System/data/data.json b/Services/Style/System/data/data.json index b783208b6199..22e71422ab42 100644 --- a/Services/Style/System/data/data.json +++ b/Services/Style/System/data/data.json @@ -1 +1 @@ -{"FactoryUIComponent":{"id":"FactoryUIComponent","title":"UIComponent","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"What is to be done by this control","composition":"What happens if the control is operated","effect":"What happens if the control is operated","rivals":{"Rival 1":"What other controls are similar, what is their distinction"}},"background ":"Relevant academic information","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Where and when an element is to be used or not."},"composition":[],"interaction":{"2":"How the interaction with this object takes place."},"wording":{"3":"How the wording of labels or captions must be."},"ordering":{"5":"How different elements of this instance are to be ordered."},"style":{"4":"How this element should look like."},"responsiveness":{"6":"How this element behaves on changing screen sizes"},"accessibility":{"7":"How this element is made accessible"}},"parent":false,"children":["CounterFactoryCounter","GlyphFactoryGlyph","ButtonFactoryButton","CardCardCard","DeckDeckDeck","ListingFactoryListing","ImageFactoryImage","LegacyLegacyLegacy","PanelFactoryPanel","ModalFactoryModal"],"less_variables":[],"path":"src\/UI\/Factory"},"CounterFactoryCounter":{"id":"CounterFactoryCounter","title":"Counter","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Counter inform users about the quantity of items indicated by a glyph.","composition":"Counters consist of a number and some background color and are placed one the 'end of the line' in reading direction of the item they state the count for.","effect":"Counters convey information, they are not interactive.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_3854_1357.html"],"rules":{"usage":{"1":"A counter MUST only be used in combination with a glyph."},"composition":{"1":"A counter MUST contain exactly one number greater than zero and no other characters."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["CounterCounterStatus","CounterCounterNovelty"],"less_variables":[],"path":"src\/UI\/Component\/Counter\/Factory"},"GlyphFactoryGlyph":{"id":"GlyphFactoryGlyph","title":"Glyph","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Glyphs map a generally known concept or symbol to a specific concept in ILIAS. Glyphs are used when space is scarce.","composition":"A glyph is a typographical character that represents something else. As any other typographical character, they can be manipulated by regular CSS. If hovered they change their background to indicate possible interactions.","effect":"Glyphs act as trigger for some action such as opening a certain Overlay type or as shortcut.","rivals":{"icon":"Standalone Icons are not interactive. Icons can be in an interactive container however. Icons merely serve as additional hint of the functionality described by some title. Glyphs are visually distinguished from object icons: they are monochrome."}},"background ":"\"In typography, a glyph is an elemental symbol within an agreed set of symbols, intended to represent a readable character for the purposes of writing and thereby expressing thoughts, ideas and concepts.\" (https:\/\/en.wikipedia.org\/wiki\/Glyph) Lidwell states that such symbols are used \"to improve the recognition and recall of signs and controls\".","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Glyphs MUST NOT be used in content titles.","2":"Glyphs MUST be used for cross-sectional functionality such as mail for example and NOT for representing objects.","3":"Glyphs SHOULD be used for very simple tasks that are repeated at many places throughout the system.","4":"Services such as mail MAY be represented by a glyph AND an icon."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"All Glyphs MUST be taken from the Bootstrap Glyphicon Halflings set. Exceptions MUST be approved by the JF."},"responsiveness":[],"accessibility":{"1":"The functionality triggered by the Glyph must be indicated to screen readers with by the attribute aria-label or aria-labelledby attribute."}},"parent":"FactoryUIComponent","children":["GlyphGlyphSettings","GlyphGlyphCollapse","GlyphGlyphExpand","GlyphGlyphAdd","GlyphGlyphRemove","GlyphGlyphUp","GlyphGlyphDown","GlyphGlyphBack","GlyphGlyphNext","GlyphGlyphSortAscending","GlyphGlyphSortDescending","GlyphGlyphUser","GlyphGlyphMail","GlyphGlyphNotification","GlyphGlyphTag","GlyphGlyphNote","GlyphGlyphComment"],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Factory"},"ButtonFactoryButton":{"id":"ButtonFactoryButton","title":"Button","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Buttons trigger interactions that change the system\u2019s status. Usually Buttons are contained in an Input Collection. The Toolbar is the main exception to this rule, since buttons in the Toolbar might also perform view changes.","composition":"Button is a clickable, graphically obtrusive control element. It can bear text.","effect":"On-click, the action indicated by the button is carried out.","rivals":{"glyph":"Glyphs are used if the enclosing Container Collection can not provide enough space for textual information or if such an information would clutter the screen.","links":"Links are used to trigger Interactions that do not change the systems status. They are usually contained inside a Navigational Collection."}},"background ":"Wording rules have been inspired by the iOS Human Interface Guidelines (UI-Elements->Controls->System Button) Style rules have been inspired from the GNOME Human Interface Guidelines->Buttons.","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Buttons MUST NOT be used inside a Textual Paragraph."},"composition":[],"interaction":{"1":"A Button SHOULD trigger an action. Only in Toolbars, Buttons MAY also change the view.","2":"If an action is temporarily not available, Buttons MUST be disabled by setting as type 'disabled'."},"wording":{"1":"The caption of a Button SHOULD contain no more than two words.","2":"The wording of the button SHOULD describe the action the button performs by using a verb or a verb phrase.","3":"Every word except articles, coordinating conjunctions and prepositions of four or fewer letters MUST be capitalized.","4":"For standard events such as saving or canceling the existing standard terms MUST be used if possible: Save, Cancel, Delete, Cut, Copy.","5":"There are cases where a non-standard label such as \u201cSend Mail\u201d for saving and sending the input of a specific form might deviate from the standard. These cases MUST however specifically justified."},"ordering":[],"style":{"1":"If Text is used inside a Button, the Button MUST be at least six characters wide."},"responsiveness":[],"accessibility":{"1":"DOM elements of type \"button\" MUST be used to properly identify an element as a Button if there is no good reason to do otherwise.","2":"Button DOM elements MUST either be of type \"button\", of type \"a\" accompanied with the aria-role \u201cButton\u201d or input along with the type attribute \u201cbutton\u201d or \"submit\"."}},"parent":"FactoryUIComponent","children":["ButtonStandardStandard","ButtonPrimaryPrimary","ButtonCloseClose"],"less_variables":[],"path":"src\/UI\/Component\/Button\/Factory"},"CardCardCard":{"id":"CardCardCard","title":"Card","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A card is a flexible content container for small chunks of structured data. Cards are often used in so-called Decks which are a gallery of Cards.","composition":"Cards contain a header, which often includes an Image or Icon and a Title as well as possible actions as Default Buttons and 0 to n sections that may contain further textual descriptions, links and buttons.","effect":"Cards may contain Interaction Triggers.","rivals":{"Heading Panel":"Heading Panels fill up the complete available width in the Center Content Section. Multiple Heading Panels are stacked vertically.","Block Panels":"Block Panels are used in Sidebars"}},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_3208_1357.html"],"rules":{"usage":[],"composition":{"1":"Cards MUST contain a title.","2":"Cards SHOULD contain an Image or Icon in the header section.","3":"Cards MAY contain Interaction Triggers."},"interaction":[],"wording":[],"ordering":[],"style":{"1":"Sections of Cards MUST be separated by Dividers."},"responsiveness":[],"accessibility":{"1":"If multiple Cards are used, they MUST be contained in a Deck."}},"parent":"FactoryUIComponent","children":[],"less_variables":[],"path":"src\/UI\/Component\/Card\/Card"},"DeckDeckDeck":{"id":"DeckDeckDeck","title":"Deck","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Decks are used to display multiple Cards in a grid. They should be used if a page contains many content items that have similar style and importance. A Deck gives each item equal horizontal space indicating that they are of equal importance.","composition":"Decks are composed only of Cards arranged in a grid. The cards displayed by decks are all of equal size. This Size ranges very small (XS) to very large (XL).","effect":"The Deck is a mere scaffolding element, is has no effect.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_3992_1357.html"],"rules":{"usage":{"1":"Decks MUST only be used to display multiple Cards."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"The number of cards displayed per row MUST adapt to the screen size."},"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":[],"less_variables":[],"path":"src\/UI\/Component\/Deck\/Deck"},"ListingFactoryListing":{"id":"ListingFactoryListing","title":"Listing","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Listings are used to structure itemised textual information.","composition":"Listings may contain ordered, unordered, or labeled items.","effect":"Listings hold only textual information. They may contain Links but no Buttons.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Listings MUST NOT contain Buttons."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["ListingUnorderedUnordered","ListingOrderedOrdered","ListingDescriptiveDescriptive"],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Factory"},"ImageFactoryImage":{"id":"ImageFactoryImage","title":"Image","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Image component is used to display images of various sources.","composition":"An Image is composed of the image and an alternative text for screen readers.","effect":"Images may be included in interacted components but not interactive on their own.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"Images MUST contain the alt attribute. This attribute MAY be left empty (alt=\"\") if the image is of decorative nature. According to the WAI, decorative images don\u2019t add information to the content of a page. For example, the information provided by the image might already be given using adjacent text, or the image might be included to make the website more visually attractive (see https:\/\/www.w3.org\/WAI\/tutorials\/images\/decorative\/<\/a>)."}},"parent":"FactoryUIComponent","children":["ImageImageStandard","ImageImageResponsive"],"less_variables":[],"path":"src\/UI\/Component\/Image\/Factory"},"LegacyLegacyLegacy":{"id":"LegacyLegacyLegacy","title":"Legacy","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"This component is used to wrap an existing ILIAS UI element into a UI component. This is useful if a container of the UI components needs to contain content that is not yet implement in the centralized UI components.","composition":"The legacy component contains html or any other content as string.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"This component MUST only be used to ensure backwards compatibility with existing UI elements in ILIAS, therefore it SHOULD only contain Elements which cannot be generated using other UI Components from the UI Service."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":[],"less_variables":[],"path":"src\/UI\/Component\/Legacy\/Legacy"},"PanelFactoryPanel":{"id":"PanelFactoryPanel","title":"Panel","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Panels are used to group titled content.","composition":"Panels consist of a header and content section. They form one Gestalt and so build a perceivable cluster of information.","effect":"The effect of interaction with panels heavily depends on their content.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":{"1":"Panels MUST contain a title."},"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["PanelStandardStandard","PanelSubSub","PanelReportReport"],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Factory"},"ModalFactoryModal":{"id":"ModalFactoryModal","title":"Modal","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Modal forces users to focus on the task at hand.","composition":"A Modal is a full-screen dialog on top of the greyed-out ILIAS screen. The Modal consists of a header with a close button and a typography modal title, a content section and might have a footer.","effect":"All controls of the original context are inaccessible until the Modal is completed. Upon completion the user returns to the original context.","rivals":{"1":"Modals have some relations to popovers. The main difference between the two is the disruptive nature of the Modal and the larger amount of data that might be displayed inside a modal. Also popovers perform mostly action to add or consult metadata of an item while Modals manipulate or focus items or their sub-items directly."}},"background ":"http:\/\/quince.infragistics.com\/Patterns\/Modal%20Panel.aspx","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The main purpose of the Modals MUST NOT be navigational. But Modals MAY be dialogue of one or two steps and thus encompass \"next\"-buttons or the like.","2":"Modals MUST NOT contain other modals (Modal in Modal).","3":"Modals SHOULD not be used to perform complex workflows.","4":"Modals MUST be closable by a little \u201cx\u201d-button on the right side of the header.","5":"Modals MUST contain a title in the header."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["ModalInterruptiveInterruptive","InterruptiveItemInterruptiveItem","ModalRoundTripRoundtrip","ModalLightboxLightbox","LightboxImagePageLightboxImagePage"],"less_variables":[],"path":"src\/UI\/Component\/Modal\/Factory"},"CounterCounterStatus":{"id":"CounterCounterStatus","title":"Status","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Status counter is used to display information about the total number of some items like users active on the system or total number of comments.","composition":"The Status Counter is a non-obstrusive Counter.","effect":"Status Counters convey information, they are not interactive.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"The Status Counter MUST be displayed on the lower right of the item it accompanies.","2":"The Status Counter SHOULD have a non-obstrusive background color, such as grey."},"responsiveness":[],"accessibility":[]},"parent":"CounterFactoryCounter","children":[],"less_variables":[],"path":"src\/UI\/Component\/Counter\/Counter"},"CounterCounterNovelty":{"id":"CounterCounterNovelty","title":"Novelty","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Novelty counters inform users about the arrival or creation of new items of the kind indicated by the accompanying glyph.","composition":"A Novelty Counter is an obtrusive counter.","effect":"They count down \/ disappear as soon as the change has been consulted by the user.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Novelty Counter MAY be used with the Status Counter."},"composition":[],"interaction":{"2":"There MUST be a way for the user to consult the changes indicated by the counter.","3":"After the consultation, the Novelty Counter SHOULD disappear or the number it contains is reduced by one.","4":"Depending on the content, the reduced number MAY be added in an additional Status Counter."},"wording":[],"ordering":[],"style":{"5":"The Novelty Counter MUST be displayed on the top at the 'end of the line' in reading direction of the item it accompanies. This would be top right for latin script and top left for arabic script.","6":"The Novelty Counter SHOULD have an obstrusive background color, such as red or orange."},"responsiveness":[],"accessibility":[]},"parent":"CounterFactoryCounter","children":[],"less_variables":[],"path":"src\/UI\/Component\/Counter\/Counter"},"GlyphGlyphSettings":{"id":"GlyphGlyphSettings","title":"Settings","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Settings Glyph triggers opening a Dropdown to edit settings of the displayed block.","composition":"The Settings Glyph uses the glyphicon-cog.","effect":"Upon clicking a settings Dropdown is opened.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Settings Glyph MUST only be used in Blocks."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u201cSettings\u201d."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphCollapse":{"id":"GlyphGlyphCollapse","title":"Collapse","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Collapse Glyph is used to trigger the collapsing of some neighbouring Container Collection such as a the content of a Dropdown or an Accordion currently shown.","composition":"The Collapse Glyph is composed of a triangle pointing to the bottom indicating that content is currently shown.","effect":"Clicking the Collapse Glyph hides the display of some Container Collection.","rivals":{"Expand Glyph":"The Expand Glyphs triggers the display of some Container Collection.","Previous Glyph":"The Previous\/Next Glyph opens a completely new view. It serves a navigational purpose."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Collapse Glyph MUST indicate if the toggled Container Collection is visible or not."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Collapse Content'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphExpand":{"id":"GlyphGlyphExpand","title":"Expand","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Expand Glyph is used to trigger the display of some neighbouring Container Collection such as a the content of a Dropdown or an Accordion currently shown.","composition":"The Expand Glyph is composed of a triangle pointing to the right indicating that content is currently shown.","effect":"Clicking the Expand Glyph displays some Container Collection.","rivals":{"Collapse Glyph":"The Collapse Glyphs hides the display of some Container Collection.","Previous Glyph":"The Previous\/Next Glyph opens a completely new view. It serves a navigational purpose."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Expand Glyph MUST indicate if the toggled Container Collection is visible or not."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Expand Content'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphAdd":{"id":"GlyphGlyphAdd","title":"Add","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The glyphed add-button serves as stand-in for the respective textual buttons in very crowded screens. It allows adding a new item.","composition":"The Add Glyph uses the glyphicon-plus-sign.","effect":"Clicking on the Add Glyph adds a new input to a form or an event to the calendar.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Add Glyph SHOULD not come without a Remove Glyph and vice versa. Because either there is not enough place for textual buttons or there is place. Exceptions to this rule, such as the Calendar, where only elements can be added in a certain place are possible, are to be run through the Jour Fixe.","2":"The Add Glyph stands for an Action and SHOULD be placed in the action column of a form.","3":"The Add Glyph MUST not be used to add lines to tables."},"composition":[],"interaction":{"1":"Newly added items MUST be placed below the line in which the Add Glyph has been clicked"},"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Add'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphRemove":{"id":"GlyphGlyphRemove","title":"Remove","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Remove Glyph serves as stand-in for the respective textual buttons in very crowded screens. It allows removing an item.","composition":"The Remove Glyph uses the glyphicon-plus-sign.","effect":"Clicking on the Remove Glyph adds a new input to a form or an event to the calendar.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Remove Glyph SHOULD not come without a glyphed Add Glyph and vice versa. Because either there is not enough place for textual buttons or there is place. Exceptions to this rule, such as the Calendar, where only elements can be added in a certain place are possible, are to be run through the Jour Fixe.","2":"The Remove Glyph stands for an Action and SHOULD be placed in the action column of a form.","3":"The Remove Glyph MUST not be used to add lines to tables."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Remove'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphUp":{"id":"GlyphGlyphUp","title":"Up","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Up Glyph allows for manually arranging rows in tables embedded in forms. It allows moving an item up.","composition":"The Up Glyph uses the glyphicon-circle-arrow-up. The Up Glyph can be combined with the Add\/Remove Glyph.","effect":"Clicking on the Up Glyph moves an item up.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_813_1357.html"],"rules":{"usage":{"1":"The Up Glyph MUST NOT be used to sort tables. There is an established sorting control for that.","2":"The Up Glyph SHOULD not come without a Down and vice versa.","3":"The Up Glyph is an action and SHOULD be listed in the action column of a form."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Up'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphDown":{"id":"GlyphGlyphDown","title":"Down","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Down Glyph allows for manually arranging rows in tables embedded in forms. It allows moving an item down.","composition":"The Down Glyph uses the glyphicon-circle-arrow-down. The Down Glyph can be combined with the Add\/Remove Glyph.","effect":"Clicking on the Down Glyph moves an item up.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_813_1357.html"],"rules":{"usage":{"1":"The Down Glyph MUST NOT be used to sort tables. There is an established sorting control for that.","2":"The Down Glyph SHOULD not come without a Up and vice versa.","3":"The Down Glyph is an action and SHOULD be listed in the action column of a form."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Down'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphBack":{"id":"GlyphGlyphBack","title":"Back","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Back Glyph indicates a possible change of the view. The view change leads back to some previous view.","composition":"The chevron-left glyphicon is used.","effect":"The click on a Back Glyph leads back to a previous view.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Back and Next Buttons MUST be accompanied by the respective Back\/Next Glyph."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"If clicking on the Back\/Next GLYPH opens a new view of an object, the Next Glyph MUST be used.","2":"If clicking on the Back\/Next GLYPH opens a previous view of an object, the Back Glyph MUST be used."},"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Back'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphNext":{"id":"GlyphGlyphNext","title":"Next","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Next Glyph indicates a possible change of the view. The view change leads back to some previous view.","composition":"The chevron-right glyphicon is used.","effect":"The click on a Next Glyph opens a new view.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Back and Next Buttons MUST be accompanied by the respective Back\/Next Glyph."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"If clicking on the Back\/Next GLYPH opens a new view of an object, the Next Glyph MUST be used.","2":"If clicking on the Back\/Next GLYPH opens a previous view of an object, the Back Glyph MUST be used."},"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Next'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphSortAscending":{"id":"GlyphGlyphSortAscending","title":"Sort Ascending","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Sorting Glyphs indicate the sorting direction of a column in a table as ascending (up) or descending (down). It is a toggle reversing the ordering of a column.","composition":"The Sort Ascending Glyph uses glyphicon-arrow-up.","effect":"Clicking the Sort Ascending Glyph reverses the direction of ordering in a table.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Sort Ascending'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphSortDescending":{"id":"GlyphGlyphSortDescending","title":"Sort Descending","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Sorting Glyphs indicate the sorting direction of a column in a table as ascending (up) or descending (down). It is a toggle reversing the ordering of a column.","composition":"The Sort Descending Glyph uses glyphicon-arrow-descending.","effect":"Clicking the Sort Descending Glyph reverses the direction of ordering in a table.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Sort Descending'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphUser":{"id":"GlyphGlyphUser","title":"User","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The User Glyph triggers the \u201cWho is online?\u201d Popover in the Top Navigation. The User Glyph indicates the number of pending contact requests and users online via the the Novelty Counter and Status Counter respectively.","composition":"The User Glyph uses the glyphicon-user.","effect":"Clicking the User Glyph opens the \u201cWho is online?\u201d Popover.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Show who is online'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphMail":{"id":"GlyphGlyphMail","title":"Mail","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Mail Glyph provides a shortcut to the mail service. The Mail Glyph indicates the number of new mails received.","composition":"The Mail Glyph uses the glyphicon-envelope.","effect":"Upon clicking on the Mail Glyph the user is transferred to the full-screen mail service.","rivals":{"Mail Icon":"The Mail Icon is used to indicate the user is currently located in the Mail service The Mail Glyph acts as shortcut to the Mail service."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Mail'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphNotification":{"id":"GlyphGlyphNotification","title":"Notification","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Notification Glyph allows users to activate \/ deactivate the notification service for a specific object or sub-item. It is a toggle indicating by colour whether it is activated or not.","composition":"The Notification Glyph uses the glyphicon-bell in link-color if notifications are not active or brand-warning color if they are.","effect":"Upon clicking the notification activation is toggled: Clicking the Notification Glyph activates respectively deactivates the notification service for the current object or sub-item.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Notification Glyph MUST only be used in the Content Top Actions."},"composition":[],"interaction":{"1":"Clicking the Notification Glyph MUST toggle the activation of Notifications."},"wording":[],"ordering":[],"style":{"1":"If notifications are activated the Notification Glyph MUST use the brand-warning color."},"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Notifications'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphTag":{"id":"GlyphGlyphTag","title":"Tag","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Tag Glyph is used to indicate the possibility of adding tags to an object.","composition":"The Tag Glyph uses the glyphicon-tag.","effect":"Upon clicking the Round Trip Modal to add new Tags is opened.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Novelty and Status Counter MUST show the amount of tags that has been given for an specific object."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Tags'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphNote":{"id":"GlyphGlyphNote","title":"Note","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Note Glyph is used to indicate the possibilty of adding notes to an object.","composition":"The Note Glyph uses the glyphicon-pushpin.","effect":"Upon clicking the Round Trip Modal to add new notes is opened","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Novelty and Status Counter MUST show the amount of notes that has been given for an specific object."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Notes'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphComment":{"id":"GlyphGlyphComment","title":"Comment","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Comment Glyph is used to indicate the possibilty of adding comments to an object.","composition":"The Comment Glyph uses the glyphicon-comment.","effect":"Upon clicking the Round Trip Modal to add new comments is opened.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Novelty and Status Counter MUST show the amount of comments that has been given for an specific object."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Comments'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"ButtonStandardStandard":{"id":"ButtonStandardStandard","title":"Standard","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The standard button is the default button to be used in ILIAS. If there is no good reason using another button instance in ILIAS, this is the one that should be used.","composition":"The standard button uses the primary color as background.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Standard buttons MUST be used if there is no good reason using another instance."},"composition":[],"interaction":[],"wording":[],"ordering":{"1":"The most important standard button SHOULD be first in reading direction if there are several buttons.","2":"In the toolbar and in forms special regulations for the ordering of the buttons MAY apply."},"style":[],"responsiveness":{"1":"The most important standard button in multi-action bars MUST be sticky (stay visible on small screens)."},"accessibility":[]},"parent":"ButtonFactoryButton","children":[],"less_variables":[],"path":"src\/UI\/Component\/Button\/Standard"},"ButtonPrimaryPrimary":{"id":"ButtonPrimaryPrimary","title":"Primary","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The primary button indicates the most important action on a screen. By definition there can only be one single \u201cmost important\u201d action on any given screen and thus only one single primary button per screen.","composition":"The background color is the btn-primary-color. This screen-unique button-color ensures that it stands out and attracts the user\u2019s attention while there are several buttons competing for attention.","effect":"In toolbars the primary button are required to be sticky, meaning they stay in view in the responsive view.","rivals":[]},"background ":"Tiddwell refers to the primary button as \u201cprominent done button\u201d and describes that \u201cthe button that finishes a transaction should be placed at the end of the visual flow; and is to be made big and well labeled.\u201d She explains that \u201cA well-understood, obvious last step gives your users a sense of closure. There\u2019s no doubt that the transaction will be done when that button is clicked; don\u2019t leave them hanging, wondering whether their work took effect\u201d. The GNOME Human Interface Guidelines -> Buttons also describes a button indicated as most important for dialogs.","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Most pages SHOULD NOT have any Primary Button at all.","2":"There MUST be no more than one Primary Button per page in ILIAS.","3":"The decision to make a Button a Primary Button MUST be confirmed by the JF."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ButtonFactoryButton","children":[],"less_variables":[],"path":"src\/UI\/Component\/Button\/Primary"},"ButtonCloseClose":{"id":"ButtonCloseClose","title":"Close","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The close button triggers the closing of some collection displayed temporarily such as an overlay.","composition":"The close button is displayed without border.","effect":"Clicking the close button closes the enclosing collection.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":{"1":"The Close Button MUST always be positioned in the top right of a collection."},"style":[],"responsiveness":[],"accessibility":{"1":"The functionality of the close button MUST be indicated for screen readers by an aria-label."}},"parent":"ButtonFactoryButton","children":[],"less_variables":[],"path":"src\/UI\/Component\/Button\/Close"},"ListingUnorderedUnordered":{"id":"ListingUnorderedUnordered","title":"Unordered","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Unordered Lists are used to display a unordered set of textual elements.","composition":"Unordered Lists are composed of a set of bullets labeling the listed items.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ListingFactoryListing","children":[],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Unordered"},"ListingOrderedOrdered":{"id":"ListingOrderedOrdered","title":"Ordered","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Ordered Lists are used to displayed a numbered set of textual elements. They are used if the order of the elements is relevant.","composition":"Ordered Lists are composed of a set of numbers labeling the items enumerated.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ListingFactoryListing","children":[],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Ordered"},"ListingDescriptiveDescriptive":{"id":"ListingDescriptiveDescriptive","title":"Descriptive","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Descriptive Lists are used to display key-value doubles of textual-information.","composition":"Descriptive Lists are composed of a key acting as title describing the type of information being displayed underneath.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ListingFactoryListing","children":[],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Descriptive"},"ImageImageStandard":{"id":"ImageImageStandard","title":"Standard","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The standard image is used if the image is to be rendered in it's the original size.","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ImageFactoryImage","children":[],"less_variables":[],"path":"src\/UI\/Component\/Image\/Image"},"ImageImageResponsive":{"id":"ImageImageResponsive","title":"Responsive","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A responsive image is to be used if the image needs to adapt to changing amount of space available.","composition":"Responsive images scale nicely to the parent element.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ImageFactoryImage","children":[],"less_variables":[],"path":"src\/UI\/Component\/Image\/Image"},"PanelStandardStandard":{"id":"PanelStandardStandard","title":"Standard","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Standard Panels are used in the Center Content section to group content.","composition":"Standard Panels consist of a title and a content section. The structure of this content might be varying from Standard Panel to Standard Panel. Standard Panels may contain Sub Panels.","effect":"","rivals":{"Cards":"Often Cards are used in Decks to display multiple uniformly structured chunks of Data horizontally and vertically."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"In Forms Standard Panels MUST be used to group different sections into Form Parts.","2":"Standard Panels SHOULD be used in the Center Content as primary Container for grouping content of varying content."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"PanelFactoryPanel","children":[],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Standard"},"PanelSubSub":{"id":"PanelSubSub","title":"Sub","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Sub Panels are used to structure the content of Standard panels further into titled sections.","composition":"Sub Panels consist of a title and a content section. They may contain a Card on their right side to display meta information about the content displayed.","effect":"","rivals":{"Standard Panel":"The Standard Panel might contain a Sub Panel.","Card":"The Sub Panels may contain one Card."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Sub Panels MUST only be inside Standard Panels"},"composition":{"1":"Sub Panels MUST NOT contain Sub Panels or Standard Panels as content."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"PanelFactoryPanel","children":[],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Sub"},"PanelReportReport":{"id":"PanelReportReport","title":"Report","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Report Panels display user-generated data combining text in lists, tables and sometimes charts. Report Panels always draw from two distinct sources: the structure \/ scaffolding of the Report Panels stems from user-generated content (i.e a question of a survey, a competence with levels) and is filled with user-generated content harvested by that very structure (i.e. participants\u2019 answers to the question, self-evaluation of competence).","composition":"They are composed of a Standard Panel which contains several Sub Panels. They might also contain a card to display information meta information in their first block.","effect":"Report Panels are predominantly used for displaying data. They may however comprise links or buttons.","rivals":{"Standard Panels":"The Report Panels contains sub panels used to structure information."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Report Panels SHOULD be used when user generated content of two sources (i.e results, guidelines in a template) is to be displayed alongside each other."},"composition":[],"interaction":{"1":"Links MAY open new views.","2":"Buttons MAY trigger actions or inline editing."},"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"PanelFactoryPanel","children":[],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Report"},"ModalInterruptiveInterruptive":{"id":"ModalInterruptiveInterruptive","title":"Interruptive","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"An Interruptive modal disrupts the user in critical situation, forcing him or her to focus on the task at hand.","composition":"The modal states why this situation needs attention and may point out consequences.","effect":"All controls of the original context are inaccessible until the modal is completed. Upon completion the user returns to the original context.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Due to the heavily disruptive nature of this type of modal it MUST be restricted to critical situations (e.g. loss of data).","2":"All actions where data is deleted from the system are considered to be critical situations and SHOULD be implemented as an Interruptive modal. Exceptions are possible if items from lists in forms are to be deleted or if the modal would heavily disrupt the workflow.","3":"Interruptive modals MUST contain a primary button continuing the action that initiated the modal (e.g. Delete the item) on the left side of the footer of the modal and a default button canceling the action on the right side of the footer.","4":"The cancel button in the footer and the close button in the header MUST NOT perform any additional action than closing the Interruptive modal."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"src\/UI\/Component\/Modal\/Interruptive"},"InterruptiveItemInterruptiveItem":{"id":"InterruptiveItemInterruptiveItem","title":"Interruptive Item","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Interruptive items are displayed in an Interruptive modal and represent the object(s) being affected by the critical action, e.g. deleting.","composition":"An Interruptive item is composed of an Id, title, description and an icon.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"An interruptive item MUST have an ID and title.","2":"An interruptive item SHOULD have an icon representing the affected object.","3":"An interruptive item MAY have a description which helps to further identify the object. If an Interruptive modal displays multiple items having the the same title, the description MUST be used in order to distinct these objects from each other.","4":"If an interruptive item represents an ILIAS object, e.g. a course, then the Id, title, description and icon of the item MUST correspond to the Id, title, description and icon from the ILIAS object."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"InterruptiveItem"},"ModalRoundTripRoundtrip":{"id":"ModalRoundTripRoundtrip","title":"Roundtrip","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Round-Trip modals are to be used if the context would be lost by performing this action otherwise. Round-Trip modals accommodate sub-workflows within an overriding workflow. The Round-Trip modal ensures that the user does not leave the trajectory of the overriding workflow. This is typically the case if an ILIAS service is being called while working in an object.","composition":"Round-Trip modals are completed by a well defined sequence of only a few steps that might be displayed on a sequence of different modals connected through some \"next\" button.","effect":"Round-Trip modals perform sub-workflow involving some kind of user input. Sub-workflow is completed and user is returned to starting point allowing for continuing the overriding workflow.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Round-Trip modals MUST contain at least two buttons at the bottom of the modals: a button to cancel (right) the workflow and a button to finish or reach the next step in the workflow (left).","2":"Round-Trip modals SHOULD be used, if the user would lose the context otherwise. If the action can be performed within the same context (e.g. add a post in a forum, edit a wiki page), a Round-Trip modal MUST NOT be used.","3":"When the workflow is completed, Round-Trip modals SHOULD show the same view that was displayed when initiating the modal.","4":"Round-Trip modals SHOULD NOT be used to add new items of any kind since adding item is a linear workflow redirecting to the newly added item setting- or content-tab.","5":"Round-Trip modals SHOULD NOT be used to perform complex workflows."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"src\/UI\/Component\/Modal\/RoundTrip"},"ModalLightboxLightbox":{"id":"ModalLightboxLightbox","title":"Lightbox","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Lightbox modal displays media data such as images or videos.","composition":"A Lightbox modal consists of one or multiple lightbox pages representing the media together with a title and description.","effect":"Lightbox modals are activated by clicking the full view glyphicon, the title of the object or it's thumbnail. If multiple pages are to be displayed, they can flipped through.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Lightbox modals MUST contain a title above the presented item.","2":"Lightbox modals SHOULD contain a descriptional text below the presented items.","3":"Multiple media items inside a Lightbox modal MUST be presented in carousel like manner allowing to flickr through items."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"src\/UI\/Component\/Modal\/Lightbox"},"LightboxImagePageLightboxImagePage":{"id":"LightboxImagePageLightboxImagePage","title":"Lightbox Image Page","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A Lightbox image page represents an image inside a Lightbox modal.","composition":"The page consists of the image, a title and optional description.","effect":"The image is displayed in the content section of the Lightbox modal and the title is used as modal title. If a description is present, it will be displayed below the image.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"2":"A Lighbox image page MUST have an image and a short title.","1":"A Lightbox image page SHOULD have short a description, describing the presented image. If the description is omitted, the Lightbox image page falls back to the alt tag of the image."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"LightboxImagePage"}} \ No newline at end of file +{"FactoryUIComponent":{"id":"FactoryUIComponent","title":"UIComponent","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"What is to be done by this control","composition":"What happens if the control is operated","effect":"What happens if the control is operated","rivals":{"Rival 1":"What other controls are similar, what is their distinction"}},"background ":"Relevant academic information","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Where and when an element is to be used or not."},"composition":[],"interaction":{"2":"How the interaction with this object takes place."},"wording":{"3":"How the wording of labels or captions must be."},"ordering":{"5":"How different elements of this instance are to be ordered."},"style":{"4":"How this element should look like."},"responsiveness":{"6":"How this element behaves on changing screen sizes"},"accessibility":{"7":"How this element is made accessible"}},"parent":false,"children":["CounterFactoryCounter","GlyphFactoryGlyph","ButtonFactoryButton","CardCardCard","DeckDeckDeck","ListingFactoryListing","ImageFactoryImage","LegacyLegacyLegacy","PanelFactoryPanel","ModalFactoryModal","InputFactoryInput"],"less_variables":[],"path":"src\/UI\/Factory"},"CounterFactoryCounter":{"id":"CounterFactoryCounter","title":"Counter","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Counter inform users about the quantity of items indicated by a glyph.","composition":"Counters consist of a number and some background color and are placed one the 'end of the line' in reading direction of the item they state the count for.","effect":"Counters convey information, they are not interactive.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_3854_1357.html"],"rules":{"usage":{"1":"A counter MUST only be used in combination with a glyph."},"composition":{"1":"A counter MUST contain exactly one number greater than zero and no other characters."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["CounterCounterStatus","CounterCounterNovelty"],"less_variables":[],"path":"src\/UI\/Component\/Counter\/Factory"},"GlyphFactoryGlyph":{"id":"GlyphFactoryGlyph","title":"Glyph","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Glyphs map a generally known concept or symbol to a specific concept in ILIAS. Glyphs are used when space is scarce.","composition":"A glyph is a typographical character that represents something else. As any other typographical character, they can be manipulated by regular CSS. If hovered they change their background to indicate possible interactions.","effect":"Glyphs act as trigger for some action such as opening a certain Overlay type or as shortcut.","rivals":{"icon":"Standalone Icons are not interactive. Icons can be in an interactive container however. Icons merely serve as additional hint of the functionality described by some title. Glyphs are visually distinguished from object icons: they are monochrome."}},"background ":"\"In typography, a glyph is an elemental symbol within an agreed set of symbols, intended to represent a readable character for the purposes of writing and thereby expressing thoughts, ideas and concepts.\" (https:\/\/en.wikipedia.org\/wiki\/Glyph) Lidwell states that such symbols are used \"to improve the recognition and recall of signs and controls\".","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Glyphs MUST NOT be used in content titles.","2":"Glyphs MUST be used for cross-sectional functionality such as mail for example and NOT for representing objects.","3":"Glyphs SHOULD be used for very simple tasks that are repeated at many places throughout the system.","4":"Services such as mail MAY be represented by a glyph AND an icon."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"All Glyphs MUST be taken from the Bootstrap Glyphicon Halflings set. Exceptions MUST be approved by the JF."},"responsiveness":[],"accessibility":{"1":"The functionality triggered by the Glyph must be indicated to screen readers with by the attribute aria-label or aria-labelledby attribute."}},"parent":"FactoryUIComponent","children":["GlyphGlyphSettings","GlyphGlyphCollapse","GlyphGlyphExpand","GlyphGlyphAdd","GlyphGlyphRemove","GlyphGlyphUp","GlyphGlyphDown","GlyphGlyphBack","GlyphGlyphNext","GlyphGlyphSortAscending","GlyphGlyphSortDescending","GlyphGlyphUser","GlyphGlyphMail","GlyphGlyphNotification","GlyphGlyphTag","GlyphGlyphNote","GlyphGlyphComment"],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Factory"},"ButtonFactoryButton":{"id":"ButtonFactoryButton","title":"Button","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Buttons trigger interactions that change the system\u2019s status. Usually Buttons are contained in an Input Collection. The Toolbar is the main exception to this rule, since buttons in the Toolbar might also perform view changes.","composition":"Button is a clickable, graphically obtrusive control element. It can bear text.","effect":"On-click, the action indicated by the button is carried out.","rivals":{"glyph":"Glyphs are used if the enclosing Container Collection can not provide enough space for textual information or if such an information would clutter the screen.","links":"Links are used to trigger Interactions that do not change the systems status. They are usually contained inside a Navigational Collection."}},"background ":"Wording rules have been inspired by the iOS Human Interface Guidelines (UI-Elements->Controls->System Button) Style rules have been inspired from the GNOME Human Interface Guidelines->Buttons.","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Buttons MUST NOT be used inside a Textual Paragraph."},"composition":[],"interaction":{"1":"A Button SHOULD trigger an action. Only in Toolbars, Buttons MAY also change the view.","2":"If an action is temporarily not available, Buttons MUST be disabled by setting as type 'disabled'."},"wording":{"1":"The caption of a Button SHOULD contain no more than two words.","2":"The wording of the button SHOULD describe the action the button performs by using a verb or a verb phrase.","3":"Every word except articles, coordinating conjunctions and prepositions of four or fewer letters MUST be capitalized.","4":"For standard events such as saving or canceling the existing standard terms MUST be used if possible: Save, Cancel, Delete, Cut, Copy.","5":"There are cases where a non-standard label such as \u201cSend Mail\u201d for saving and sending the input of a specific form might deviate from the standard. These cases MUST however specifically justified."},"ordering":[],"style":{"1":"If Text is used inside a Button, the Button MUST be at least six characters wide."},"responsiveness":[],"accessibility":{"1":"DOM elements of type \"button\" MUST be used to properly identify an element as a Button if there is no good reason to do otherwise.","2":"Button DOM elements MUST either be of type \"button\", of type \"a\" accompanied with the aria-role \u201cButton\u201d or input along with the type attribute \u201cbutton\u201d or \"submit\"."}},"parent":"FactoryUIComponent","children":["ButtonStandardStandard","ButtonPrimaryPrimary","ButtonCloseClose"],"less_variables":[],"path":"src\/UI\/Component\/Button\/Factory"},"CardCardCard":{"id":"CardCardCard","title":"Card","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A card is a flexible content container for small chunks of structured data. Cards are often used in so-called Decks which are a gallery of Cards.","composition":"Cards contain a header, which often includes an Image or Icon and a Title as well as possible actions as Default Buttons and 0 to n sections that may contain further textual descriptions, links and buttons.","effect":"Cards may contain Interaction Triggers.","rivals":{"Heading Panel":"Heading Panels fill up the complete available width in the Center Content Section. Multiple Heading Panels are stacked vertically.","Block Panels":"Block Panels are used in Sidebars"}},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_3208_1357.html"],"rules":{"usage":[],"composition":{"1":"Cards MUST contain a title.","2":"Cards SHOULD contain an Image or Icon in the header section.","3":"Cards MAY contain Interaction Triggers."},"interaction":[],"wording":[],"ordering":[],"style":{"1":"Sections of Cards MUST be separated by Dividers."},"responsiveness":[],"accessibility":{"1":"If multiple Cards are used, they MUST be contained in a Deck."}},"parent":"FactoryUIComponent","children":[],"less_variables":[],"path":"src\/UI\/Component\/Card\/Card"},"DeckDeckDeck":{"id":"DeckDeckDeck","title":"Deck","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Decks are used to display multiple Cards in a grid. They should be used if a page contains many content items that have similar style and importance. A Deck gives each item equal horizontal space indicating that they are of equal importance.","composition":"Decks are composed only of Cards arranged in a grid. The cards displayed by decks are all of equal size. This Size ranges very small (XS) to very large (XL).","effect":"The Deck is a mere scaffolding element, is has no effect.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_3992_1357.html"],"rules":{"usage":{"1":"Decks MUST only be used to display multiple Cards."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"The number of cards displayed per row MUST adapt to the screen size."},"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":[],"less_variables":[],"path":"src\/UI\/Component\/Deck\/Deck"},"ListingFactoryListing":{"id":"ListingFactoryListing","title":"Listing","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Listings are used to structure itemised textual information.","composition":"Listings may contain ordered, unordered, or labeled items.","effect":"Listings hold only textual information. They may contain Links but no Buttons.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Listings MUST NOT contain Buttons."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["ListingUnorderedUnordered","ListingOrderedOrdered","ListingDescriptiveDescriptive"],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Factory"},"ImageFactoryImage":{"id":"ImageFactoryImage","title":"Image","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Image component is used to display images of various sources.","composition":"An Image is composed of the image and an alternative text for screen readers.","effect":"Images may be included in interacted components but not interactive on their own.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"Images MUST contain the alt attribute. This attribute MAY be left empty (alt=\"\") if the image is of decorative nature. According to the WAI, decorative images don\u2019t add information to the content of a page. For example, the information provided by the image might already be given using adjacent text, or the image might be included to make the website more visually attractive (see https:\/\/www.w3.org\/WAI\/tutorials\/images\/decorative\/<\/a>)."}},"parent":"FactoryUIComponent","children":["ImageImageStandard","ImageImageResponsive"],"less_variables":[],"path":"src\/UI\/Component\/Image\/Factory"},"LegacyLegacyLegacy":{"id":"LegacyLegacyLegacy","title":"Legacy","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"This component is used to wrap an existing ILIAS UI element into a UI component. This is useful if a container of the UI components needs to contain content that is not yet implement in the centralized UI components.","composition":"The legacy component contains html or any other content as string.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"This component MUST only be used to ensure backwards compatibility with existing UI elements in ILIAS, therefore it SHOULD only contain Elements which cannot be generated using other UI Components from the UI Service."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":[],"less_variables":[],"path":"src\/UI\/Component\/Legacy\/Legacy"},"PanelFactoryPanel":{"id":"PanelFactoryPanel","title":"Panel","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Panels are used to group titled content.","composition":"Panels consist of a header and content section. They form one Gestalt and so build a perceivable cluster of information.","effect":"The effect of interaction with panels heavily depends on their content.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":{"1":"Panels MUST contain a title."},"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["PanelStandardStandard","PanelSubSub","PanelReportReport"],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Factory"},"ModalFactoryModal":{"id":"ModalFactoryModal","title":"Modal","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Modal forces users to focus on the task at hand.","composition":"A Modal is a full-screen dialog on top of the greyed-out ILIAS screen. The Modal consists of a header with a close button and a typography modal title, a content section and might have a footer.","effect":"All controls of the original context are inaccessible until the Modal is completed. Upon completion the user returns to the original context.","rivals":{"1":"Modals have some relations to popovers. The main difference between the two is the disruptive nature of the Modal and the larger amount of data that might be displayed inside a modal. Also popovers perform mostly action to add or consult metadata of an item while Modals manipulate or focus items or their sub-items directly."}},"background ":"http:\/\/quince.infragistics.com\/Patterns\/Modal%20Panel.aspx","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The main purpose of the Modals MUST NOT be navigational. But Modals MAY be dialogue of one or two steps and thus encompass \"next\"-buttons or the like.","2":"Modals MUST NOT contain other modals (Modal in Modal).","3":"Modals SHOULD not be used to perform complex workflows.","4":"Modals MUST be closable by a little \u201cx\u201d-button on the right side of the header.","5":"Modals MUST contain a title in the header."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"FactoryUIComponent","children":["ModalInterruptiveInterruptive","InterruptiveItemInterruptiveItem","ModalRoundTripRoundtrip","ModalLightboxLightbox","LightboxImagePageLightboxImagePage"],"less_variables":[],"path":"src\/UI\/Component\/Modal\/Factory"},"InputFactoryInput":{"id":"InputFactoryInput","title":"Input","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Inputs capture data from users. Some Inputs capture well-structured data i.e. users choose from a set of options. Other Inputs capture data that is actively produced by users i.e. text or file uploads.","composition":"The composition of the inputs heavily depend on the exact type.","effect":"Entering input has per default no effect. E.g. in Forms, input has to be applied by clicking a \u201cSave\u201d-button to become effective. Thus Inputs are mostly accompanied by a button or contain one themselves.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"Disabled Input Elements MUST be indicated by setting the \u201cdisabled\u201d attribute.","2":"If focused, the Input Elements MUST change their Input Border Color to the Input Focus Border Color."},"responsiveness":[],"accessibility":{"1":"All Input Elements visible in a view MUST be accessible by keyboard by using the \u2018Tab\u2019-Key."}},"parent":"FactoryUIComponent","children":["InputContainerFactoryContainer","InputItemFactoryItem","InputValidationFactoryValidation"],"less_variables":[],"path":"src\/UI\/Component\/Input\/Factory"},"CounterCounterStatus":{"id":"CounterCounterStatus","title":"Status","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Status counter is used to display information about the total number of some items like users active on the system or total number of comments.","composition":"The Status Counter is a non-obstrusive Counter.","effect":"Status Counters convey information, they are not interactive.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"The Status Counter MUST be displayed on the lower right of the item it accompanies.","2":"The Status Counter SHOULD have a non-obstrusive background color, such as grey."},"responsiveness":[],"accessibility":[]},"parent":"CounterFactoryCounter","children":[],"less_variables":[],"path":"src\/UI\/Component\/Counter\/Counter"},"CounterCounterNovelty":{"id":"CounterCounterNovelty","title":"Novelty","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Novelty counters inform users about the arrival or creation of new items of the kind indicated by the accompanying glyph.","composition":"A Novelty Counter is an obtrusive counter.","effect":"They count down \/ disappear as soon as the change has been consulted by the user.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Novelty Counter MAY be used with the Status Counter."},"composition":[],"interaction":{"2":"There MUST be a way for the user to consult the changes indicated by the counter.","3":"After the consultation, the Novelty Counter SHOULD disappear or the number it contains is reduced by one.","4":"Depending on the content, the reduced number MAY be added in an additional Status Counter."},"wording":[],"ordering":[],"style":{"5":"The Novelty Counter MUST be displayed on the top at the 'end of the line' in reading direction of the item it accompanies. This would be top right for latin script and top left for arabic script.","6":"The Novelty Counter SHOULD have an obstrusive background color, such as red or orange."},"responsiveness":[],"accessibility":[]},"parent":"CounterFactoryCounter","children":[],"less_variables":[],"path":"src\/UI\/Component\/Counter\/Counter"},"GlyphGlyphSettings":{"id":"GlyphGlyphSettings","title":"Settings","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Settings Glyph triggers opening a Dropdown to edit settings of the displayed block.","composition":"The Settings Glyph uses the glyphicon-cog.","effect":"Upon clicking a settings Dropdown is opened.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Settings Glyph MUST only be used in Blocks."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u201cSettings\u201d."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphCollapse":{"id":"GlyphGlyphCollapse","title":"Collapse","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Collapse Glyph is used to trigger the collapsing of some neighbouring Container Collection such as a the content of a Dropdown or an Accordion currently shown.","composition":"The Collapse Glyph is composed of a triangle pointing to the bottom indicating that content is currently shown.","effect":"Clicking the Collapse Glyph hides the display of some Container Collection.","rivals":{"Expand Glyph":"The Expand Glyphs triggers the display of some Container Collection.","Previous Glyph":"The Previous\/Next Glyph opens a completely new view. It serves a navigational purpose."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Collapse Glyph MUST indicate if the toggled Container Collection is visible or not."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Collapse Content'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphExpand":{"id":"GlyphGlyphExpand","title":"Expand","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Expand Glyph is used to trigger the display of some neighbouring Container Collection such as a the content of a Dropdown or an Accordion currently shown.","composition":"The Expand Glyph is composed of a triangle pointing to the right indicating that content is currently shown.","effect":"Clicking the Expand Glyph displays some Container Collection.","rivals":{"Collapse Glyph":"The Collapse Glyphs hides the display of some Container Collection.","Previous Glyph":"The Previous\/Next Glyph opens a completely new view. It serves a navigational purpose."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Expand Glyph MUST indicate if the toggled Container Collection is visible or not."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Expand Content'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphAdd":{"id":"GlyphGlyphAdd","title":"Add","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The glyphed add-button serves as stand-in for the respective textual buttons in very crowded screens. It allows adding a new item.","composition":"The Add Glyph uses the glyphicon-plus-sign.","effect":"Clicking on the Add Glyph adds a new input to a form or an event to the calendar.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Add Glyph SHOULD not come without a Remove Glyph and vice versa. Because either there is not enough place for textual buttons or there is place. Exceptions to this rule, such as the Calendar, where only elements can be added in a certain place are possible, are to be run through the Jour Fixe.","2":"The Add Glyph stands for an Action and SHOULD be placed in the action column of a form.","3":"The Add Glyph MUST not be used to add lines to tables."},"composition":[],"interaction":{"1":"Newly added items MUST be placed below the line in which the Add Glyph has been clicked"},"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Add'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphRemove":{"id":"GlyphGlyphRemove","title":"Remove","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Remove Glyph serves as stand-in for the respective textual buttons in very crowded screens. It allows removing an item.","composition":"The Remove Glyph uses the glyphicon-plus-sign.","effect":"Clicking on the Remove Glyph adds a new input to a form or an event to the calendar.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Remove Glyph SHOULD not come without a glyphed Add Glyph and vice versa. Because either there is not enough place for textual buttons or there is place. Exceptions to this rule, such as the Calendar, where only elements can be added in a certain place are possible, are to be run through the Jour Fixe.","2":"The Remove Glyph stands for an Action and SHOULD be placed in the action column of a form.","3":"The Remove Glyph MUST not be used to add lines to tables."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Remove'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphUp":{"id":"GlyphGlyphUp","title":"Up","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Up Glyph allows for manually arranging rows in tables embedded in forms. It allows moving an item up.","composition":"The Up Glyph uses the glyphicon-circle-arrow-up. The Up Glyph can be combined with the Add\/Remove Glyph.","effect":"Clicking on the Up Glyph moves an item up.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_813_1357.html"],"rules":{"usage":{"1":"The Up Glyph MUST NOT be used to sort tables. There is an established sorting control for that.","2":"The Up Glyph SHOULD not come without a Down and vice versa.","3":"The Up Glyph is an action and SHOULD be listed in the action column of a form."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Up'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphDown":{"id":"GlyphGlyphDown","title":"Down","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Down Glyph allows for manually arranging rows in tables embedded in forms. It allows moving an item down.","composition":"The Down Glyph uses the glyphicon-circle-arrow-down. The Down Glyph can be combined with the Add\/Remove Glyph.","effect":"Clicking on the Down Glyph moves an item up.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":["http:\/\/www.ilias.de\/docu\/goto_docu_wiki_wpage_813_1357.html"],"rules":{"usage":{"1":"The Down Glyph MUST NOT be used to sort tables. There is an established sorting control for that.","2":"The Down Glyph SHOULD not come without a Up and vice versa.","3":"The Down Glyph is an action and SHOULD be listed in the action column of a form."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Down'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphBack":{"id":"GlyphGlyphBack","title":"Back","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Back Glyph indicates a possible change of the view. The view change leads back to some previous view.","composition":"The chevron-left glyphicon is used.","effect":"The click on a Back Glyph leads back to a previous view.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Back and Next Buttons MUST be accompanied by the respective Back\/Next Glyph."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"If clicking on the Back\/Next GLYPH opens a new view of an object, the Next Glyph MUST be used.","2":"If clicking on the Back\/Next GLYPH opens a previous view of an object, the Back Glyph MUST be used."},"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Back'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphNext":{"id":"GlyphGlyphNext","title":"Next","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Next Glyph indicates a possible change of the view. The view change leads back to some previous view.","composition":"The chevron-right glyphicon is used.","effect":"The click on a Next Glyph opens a new view.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Back and Next Buttons MUST be accompanied by the respective Back\/Next Glyph."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":{"1":"If clicking on the Back\/Next GLYPH opens a new view of an object, the Next Glyph MUST be used.","2":"If clicking on the Back\/Next GLYPH opens a previous view of an object, the Back Glyph MUST be used."},"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Next'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphSortAscending":{"id":"GlyphGlyphSortAscending","title":"Sort Ascending","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Sorting Glyphs indicate the sorting direction of a column in a table as ascending (up) or descending (down). It is a toggle reversing the ordering of a column.","composition":"The Sort Ascending Glyph uses glyphicon-arrow-up.","effect":"Clicking the Sort Ascending Glyph reverses the direction of ordering in a table.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Sort Ascending'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphSortDescending":{"id":"GlyphGlyphSortDescending","title":"Sort Descending","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Sorting Glyphs indicate the sorting direction of a column in a table as ascending (up) or descending (down). It is a toggle reversing the ordering of a column.","composition":"The Sort Descending Glyph uses glyphicon-arrow-descending.","effect":"Clicking the Sort Descending Glyph reverses the direction of ordering in a table.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Sort Descending'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphUser":{"id":"GlyphGlyphUser","title":"User","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The User Glyph triggers the \u201cWho is online?\u201d Popover in the Top Navigation. The User Glyph indicates the number of pending contact requests and users online via the the Novelty Counter and Status Counter respectively.","composition":"The User Glyph uses the glyphicon-user.","effect":"Clicking the User Glyph opens the \u201cWho is online?\u201d Popover.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Show who is online'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphMail":{"id":"GlyphGlyphMail","title":"Mail","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Mail Glyph provides a shortcut to the mail service. The Mail Glyph indicates the number of new mails received.","composition":"The Mail Glyph uses the glyphicon-envelope.","effect":"Upon clicking on the Mail Glyph the user is transferred to the full-screen mail service.","rivals":{"Mail Icon":"The Mail Icon is used to indicate the user is currently located in the Mail service The Mail Glyph acts as shortcut to the Mail service."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Mail'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphNotification":{"id":"GlyphGlyphNotification","title":"Notification","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Notification Glyph allows users to activate \/ deactivate the notification service for a specific object or sub-item. It is a toggle indicating by colour whether it is activated or not.","composition":"The Notification Glyph uses the glyphicon-bell in link-color if notifications are not active or brand-warning color if they are.","effect":"Upon clicking the notification activation is toggled: Clicking the Notification Glyph activates respectively deactivates the notification service for the current object or sub-item.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"The Notification Glyph MUST only be used in the Content Top Actions."},"composition":[],"interaction":{"1":"Clicking the Notification Glyph MUST toggle the activation of Notifications."},"wording":[],"ordering":[],"style":{"1":"If notifications are activated the Notification Glyph MUST use the brand-warning color."},"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Notifications'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphTag":{"id":"GlyphGlyphTag","title":"Tag","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Tag Glyph is used to indicate the possibility of adding tags to an object.","composition":"The Tag Glyph uses the glyphicon-tag.","effect":"Upon clicking the Round Trip Modal to add new Tags is opened.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Novelty and Status Counter MUST show the amount of tags that has been given for an specific object."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Tags'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphNote":{"id":"GlyphGlyphNote","title":"Note","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Note Glyph is used to indicate the possibilty of adding notes to an object.","composition":"The Note Glyph uses the glyphicon-pushpin.","effect":"Upon clicking the Round Trip Modal to add new notes is opened","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Novelty and Status Counter MUST show the amount of notes that has been given for an specific object."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Notes'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"GlyphGlyphComment":{"id":"GlyphGlyphComment","title":"Comment","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Comment Glyph is used to indicate the possibilty of adding comments to an object.","composition":"The Comment Glyph uses the glyphicon-comment.","effect":"Upon clicking the Round Trip Modal to add new comments is opened.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Novelty and Status Counter MUST show the amount of comments that has been given for an specific object."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"The aria-label MUST be \u2018Comments'."}},"parent":"GlyphFactoryGlyph","children":[],"less_variables":[],"path":"src\/UI\/Component\/Glyph\/Glyph"},"ButtonStandardStandard":{"id":"ButtonStandardStandard","title":"Standard","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The standard button is the default button to be used in ILIAS. If there is no good reason using another button instance in ILIAS, this is the one that should be used.","composition":"The standard button uses the primary color as background.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Standard buttons MUST be used if there is no good reason using another instance."},"composition":[],"interaction":[],"wording":[],"ordering":{"1":"The most important standard button SHOULD be first in reading direction if there are several buttons.","2":"In the toolbar and in forms special regulations for the ordering of the buttons MAY apply."},"style":[],"responsiveness":{"1":"The most important standard button in multi-action bars MUST be sticky (stay visible on small screens)."},"accessibility":[]},"parent":"ButtonFactoryButton","children":[],"less_variables":[],"path":"src\/UI\/Component\/Button\/Standard"},"ButtonPrimaryPrimary":{"id":"ButtonPrimaryPrimary","title":"Primary","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The primary button indicates the most important action on a screen. By definition there can only be one single \u201cmost important\u201d action on any given screen and thus only one single primary button per screen.","composition":"The background color is the btn-primary-color. This screen-unique button-color ensures that it stands out and attracts the user\u2019s attention while there are several buttons competing for attention.","effect":"In toolbars the primary button are required to be sticky, meaning they stay in view in the responsive view.","rivals":[]},"background ":"Tiddwell refers to the primary button as \u201cprominent done button\u201d and describes that \u201cthe button that finishes a transaction should be placed at the end of the visual flow; and is to be made big and well labeled.\u201d She explains that \u201cA well-understood, obvious last step gives your users a sense of closure. There\u2019s no doubt that the transaction will be done when that button is clicked; don\u2019t leave them hanging, wondering whether their work took effect\u201d. The GNOME Human Interface Guidelines -> Buttons also describes a button indicated as most important for dialogs.","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Most pages SHOULD NOT have any Primary Button at all.","2":"There MUST be no more than one Primary Button per page in ILIAS.","3":"The decision to make a Button a Primary Button MUST be confirmed by the JF."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ButtonFactoryButton","children":[],"less_variables":[],"path":"src\/UI\/Component\/Button\/Primary"},"ButtonCloseClose":{"id":"ButtonCloseClose","title":"Close","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The close button triggers the closing of some collection displayed temporarily such as an overlay.","composition":"The close button is displayed without border.","effect":"Clicking the close button closes the enclosing collection.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":{"1":"The Close Button MUST always be positioned in the top right of a collection."},"style":[],"responsiveness":[],"accessibility":{"1":"The functionality of the close button MUST be indicated for screen readers by an aria-label."}},"parent":"ButtonFactoryButton","children":[],"less_variables":[],"path":"src\/UI\/Component\/Button\/Close"},"ListingUnorderedUnordered":{"id":"ListingUnorderedUnordered","title":"Unordered","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Unordered Lists are used to display a unordered set of textual elements.","composition":"Unordered Lists are composed of a set of bullets labeling the listed items.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ListingFactoryListing","children":[],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Unordered"},"ListingOrderedOrdered":{"id":"ListingOrderedOrdered","title":"Ordered","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Ordered Lists are used to displayed a numbered set of textual elements. They are used if the order of the elements is relevant.","composition":"Ordered Lists are composed of a set of numbers labeling the items enumerated.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ListingFactoryListing","children":[],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Ordered"},"ListingDescriptiveDescriptive":{"id":"ListingDescriptiveDescriptive","title":"Descriptive","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Descriptive Lists are used to display key-value doubles of textual-information.","composition":"Descriptive Lists are composed of a key acting as title describing the type of information being displayed underneath.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ListingFactoryListing","children":[],"less_variables":[],"path":"src\/UI\/Component\/Listing\/Descriptive"},"ImageImageStandard":{"id":"ImageImageStandard","title":"Standard","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The standard image is used if the image is to be rendered in it's the original size.","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ImageFactoryImage","children":[],"less_variables":[],"path":"src\/UI\/Component\/Image\/Image"},"ImageImageResponsive":{"id":"ImageImageResponsive","title":"Responsive","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A responsive image is to be used if the image needs to adapt to changing amount of space available.","composition":"Responsive images scale nicely to the parent element.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"ImageFactoryImage","children":[],"less_variables":[],"path":"src\/UI\/Component\/Image\/Image"},"PanelStandardStandard":{"id":"PanelStandardStandard","title":"Standard","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Standard Panels are used in the Center Content section to group content.","composition":"Standard Panels consist of a title and a content section. The structure of this content might be varying from Standard Panel to Standard Panel. Standard Panels may contain Sub Panels.","effect":"","rivals":{"Cards":"Often Cards are used in Decks to display multiple uniformly structured chunks of Data horizontally and vertically."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"In Forms Standard Panels MUST be used to group different sections into Form Parts.","2":"Standard Panels SHOULD be used in the Center Content as primary Container for grouping content of varying content."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"PanelFactoryPanel","children":[],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Standard"},"PanelSubSub":{"id":"PanelSubSub","title":"Sub","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Sub Panels are used to structure the content of Standard panels further into titled sections.","composition":"Sub Panels consist of a title and a content section. They may contain a Card on their right side to display meta information about the content displayed.","effect":"","rivals":{"Standard Panel":"The Standard Panel might contain a Sub Panel.","Card":"The Sub Panels may contain one Card."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Sub Panels MUST only be inside Standard Panels"},"composition":{"1":"Sub Panels MUST NOT contain Sub Panels or Standard Panels as content."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"PanelFactoryPanel","children":[],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Sub"},"PanelReportReport":{"id":"PanelReportReport","title":"Report","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Report Panels display user-generated data combining text in lists, tables and sometimes charts. Report Panels always draw from two distinct sources: the structure \/ scaffolding of the Report Panels stems from user-generated content (i.e a question of a survey, a competence with levels) and is filled with user-generated content harvested by that very structure (i.e. participants\u2019 answers to the question, self-evaluation of competence).","composition":"They are composed of a Standard Panel which contains several Sub Panels. They might also contain a card to display information meta information in their first block.","effect":"Report Panels are predominantly used for displaying data. They may however comprise links or buttons.","rivals":{"Standard Panels":"The Report Panels contains sub panels used to structure information."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Report Panels SHOULD be used when user generated content of two sources (i.e results, guidelines in a template) is to be displayed alongside each other."},"composition":[],"interaction":{"1":"Links MAY open new views.","2":"Buttons MAY trigger actions or inline editing."},"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"PanelFactoryPanel","children":[],"less_variables":[],"path":"src\/UI\/Component\/Panel\/Report"},"ModalInterruptiveInterruptive":{"id":"ModalInterruptiveInterruptive","title":"Interruptive","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"An Interruptive modal disrupts the user in critical situation, forcing him or her to focus on the task at hand.","composition":"The modal states why this situation needs attention and may point out consequences.","effect":"All controls of the original context are inaccessible until the modal is completed. Upon completion the user returns to the original context.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Due to the heavily disruptive nature of this type of modal it MUST be restricted to critical situations (e.g. loss of data).","2":"All actions where data is deleted from the system are considered to be critical situations and SHOULD be implemented as an Interruptive modal. Exceptions are possible if items from lists in forms are to be deleted or if the modal would heavily disrupt the workflow.","3":"Interruptive modals MUST contain a primary button continuing the action that initiated the modal (e.g. Delete the item) on the left side of the footer of the modal and a default button canceling the action on the right side of the footer.","4":"The cancel button in the footer and the close button in the header MUST NOT perform any additional action than closing the Interruptive modal."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"src\/UI\/Component\/Modal\/Interruptive"},"InterruptiveItemInterruptiveItem":{"id":"InterruptiveItemInterruptiveItem","title":"Interruptive Item","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Interruptive items are displayed in an Interruptive modal and represent the object(s) being affected by the critical action, e.g. deleting.","composition":"An Interruptive item is composed of an Id, title, description and an icon.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"An interruptive item MUST have an ID and title.","2":"An interruptive item SHOULD have an icon representing the affected object.","3":"An interruptive item MAY have a description which helps to further identify the object. If an Interruptive modal displays multiple items having the the same title, the description MUST be used in order to distinct these objects from each other.","4":"If an interruptive item represents an ILIAS object, e.g. a course, then the Id, title, description and icon of the item MUST correspond to the Id, title, description and icon from the ILIAS object."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"InterruptiveItem"},"ModalRoundTripRoundtrip":{"id":"ModalRoundTripRoundtrip","title":"Roundtrip","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Round-Trip modals are to be used if the context would be lost by performing this action otherwise. Round-Trip modals accommodate sub-workflows within an overriding workflow. The Round-Trip modal ensures that the user does not leave the trajectory of the overriding workflow. This is typically the case if an ILIAS service is being called while working in an object.","composition":"Round-Trip modals are completed by a well defined sequence of only a few steps that might be displayed on a sequence of different modals connected through some \"next\" button.","effect":"Round-Trip modals perform sub-workflow involving some kind of user input. Sub-workflow is completed and user is returned to starting point allowing for continuing the overriding workflow.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Round-Trip modals MUST contain at least two buttons at the bottom of the modals: a button to cancel (right) the workflow and a button to finish or reach the next step in the workflow (left).","2":"Round-Trip modals SHOULD be used, if the user would lose the context otherwise. If the action can be performed within the same context (e.g. add a post in a forum, edit a wiki page), a Round-Trip modal MUST NOT be used.","3":"When the workflow is completed, Round-Trip modals SHOULD show the same view that was displayed when initiating the modal.","4":"Round-Trip modals SHOULD NOT be used to add new items of any kind since adding item is a linear workflow redirecting to the newly added item setting- or content-tab.","5":"Round-Trip modals SHOULD NOT be used to perform complex workflows."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"src\/UI\/Component\/Modal\/RoundTrip"},"ModalLightboxLightbox":{"id":"ModalLightboxLightbox","title":"Lightbox","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The Lightbox modal displays media data such as images or videos.","composition":"A Lightbox modal consists of one or multiple lightbox pages representing the media together with a title and description.","effect":"Lightbox modals are activated by clicking the full view glyphicon, the title of the object or it's thumbnail. If multiple pages are to be displayed, they can flipped through.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Lightbox modals MUST contain a title above the presented item.","2":"Lightbox modals SHOULD contain a descriptional text below the presented items.","3":"Multiple media items inside a Lightbox modal MUST be presented in carousel like manner allowing to flickr through items."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"src\/UI\/Component\/Modal\/Lightbox"},"LightboxImagePageLightboxImagePage":{"id":"LightboxImagePageLightboxImagePage","title":"Lightbox Image Page","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A Lightbox image page represents an image inside a Lightbox modal.","composition":"The page consists of the image, a title and optional description.","effect":"The image is displayed in the content section of the Lightbox modal and the title is used as modal title. If a description is present, it will be displayed below the image.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"2":"A Lighbox image page MUST have an image and a short title.","1":"A Lightbox image page SHOULD have short a description, describing the presented image. If the description is omitted, the Lightbox image page falls back to the alt tag of the image."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"ModalFactoryModal","children":[],"less_variables":[],"path":"LightboxImagePage"},"InputContainerFactoryContainer":{"id":"InputContainerFactoryContainer","title":"Container","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Input Containers are aggregate controls in order to gather input from the user. Examples are Forms or Filters.","composition":"They consist of one or multiple Input items.","effect":"Input entered into in Input Collection is saved by clicking on an Interaction Trigger, mostly a Button.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"Every Input Item in every Input Collection MUST be accessible by keyboard."}},"parent":"InputFactoryInput","children":["ContainerFilterFilterFilter","InputContainerFormFactoryForm"],"less_variables":[],"path":"src\/UI\/Component\/Input\/Container\/Factory"},"InputItemFactoryItem":{"id":"InputItemFactoryItem","title":"Item","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"An Input Item is used to collect one specific piece of information from the user.","composition":"A Input item is composed of an label naming the concrete item, might contain a by line describing it and an input control the collect the actual data from the user.","effect":"The effect depends on the control-type of the input element.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"A Input Item MUST use a label."},"interaction":[],"wording":{"1":"A label MUST be composed of one single term or a very short phrase. The label is an eye catcher for users skimming the form.","2":"A label MUST avoid lingo. Intelligibility by occasional users is prioritized over technical accuracy. The accurate technical expression is to be mentioned in the by-line.","3":"An label MUST make a positive statement. If the purpose of the setting is inherently negative, use Verbs as \u201cLimit..\u201d, \u201cLock..\u201d.","4":"Wording and structure (sequence and control) MUST be consistent with identifiers in other objects. If you feel a wording needs to be changed, then you MUST propose it to the JF.","5":"A by-line (explanatory text) MAY be added below the input element.","6":"If by-lines are provided they MUST be informative, not merely repeating the identifier\u2019s or input element\u2019s content. If no informative description can be devised, no description is needed.","7":"A by-line MUST clearly state what effect the Setting produces and explain, why this might be important and what it can be used for.","8":"Bulk by-lines underneath a stack of option explaining all of the options in one paragraph MUST NOT be used.","9":"A by-line SHOULD NOT address the user directly. Addressing users directly is reserved for cases of high risk of severe mis-configuration.","10":"A by-line MUST be grammatically complete sentence with a period (.) at the end.","11":"By-lines SHOULD be short with no more than 25 words.","12":"By-lines SHOULD NOT use any formatting in descriptions (bold, italic or similar).","13":"If by-lines refer to other tabs or options or tables by name, that reference should be made in quotation marks: \u2018Info\u2019-tab \/ \"Info\"-Reiter, button \u2018Show Test Results\u2019 \/ Button \"Testergebnisse anzeigen\", \u2018Table of Detailed Test Results\u2019 \/ \"Tabelle mit detaillierten Testergebnissen\". Use proper quotation marks, not apostrophs. Use single quotation marks for english language and double quotation maks for german language.","14":"By-lines MUST NOT feature parentheses since they greatly diminish readability.","15":"By-lines SHOULD NOT start with terms such as: If this option is set \u2026 If this setting is active \u2026 Choose this setting if \u2026 This setting \u2026 Rather state what happens directly: Participants get \/ make \/ can \u2026 Point in time after which\u2026. ILIAS will monitor\u2026 Sub-items xy are automatically whatever .. Xy will be displayed at place."},"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"InputFactoryInput","children":["InputItemSelectorFactorySelector","InputItemFieldFactoryField"],"less_variables":[],"path":"src\/UI\/Component\/Input\/Item\/Factory"},"InputValidationFactoryValidation":{"id":"InputValidationFactoryValidation","title":"Validation","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Validation components are used to validate various kinds of inputs.","composition":"Validation components are usually placed inside of other inputs components. They can not work on their own.","effect":"Validations show whether an input is (or will be) accepted by the system or is (or was) rejected.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"InputFactoryInput","children":["InputValidationNotEmptyNotEmpty","InputValidationRegexEquals","InputValidationRegexRegex","InputValidationCustomCustom"],"less_variables":[],"path":"src\/UI\/Component\/Input\/Validation\/Factory"},"ContainerFilterFilterFilter":{"id":"ContainerFilterFilterFilter","title":"Filter","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Filters enable to add and remove criteria for displaying or hiding items in a list or table.","composition":"Filters are composed of the input selectors and fields for composing the individual criteria for the filter along with an \u201cApply Filter\u201d Button to apply Filter Button.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"Every Input Element in every Input Collection MUST be accessible by keyboard."}},"parent":"InputContainerFactoryContainer","children":[],"less_variables":[],"path":"src\/UI\/Component\/Container\/Filter\/Filter"},"InputContainerFormFactoryForm":{"id":"InputContainerFormFactoryForm","title":"Form","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Forms are used for creating content of sub-items or for configuring objects or services.","composition":"A form is divided into areas, which are further divided into sections containing settings with selection or input field controls.","effect":"Users manipulate input in settings and save the form to apply the settings to the object or service.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Forms MUST NOT be used on the same content screen as tables.","2":"Forms MUST NOT be used on the same content screen as toolbars."},"composition":{"1":"Each form MUST contain at least one titled form section."},"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"InputContainerFactoryContainer","children":["InputContainerFormStandardStandard","InputContainerFormSectionSection","InputContainerFormSubSub"],"less_variables":[],"path":"src\/UI\/Component\/Input\/Container\/Form\/Factory"},"InputContainerFormStandardStandard":{"id":"InputContainerFormStandardStandard","title":"Standard","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Currently the only form available in ILIAS","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"InputContainerFormFactoryForm","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Container\/Form\/Standard"},"InputContainerFormSectionSection":{"id":"InputContainerFormSectionSection","title":"Section","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A titled form section subdivides a form allowing users to browse the screen. Titled Form Sections are stacked. The stacking reflects perceived relevance and is kept consistent across objects thus learners are \u201cfamiliar\u201d with forms.","composition":"At least one titled form section is used in every form. A title states the purpose shared by all settings of the titled form section. The section visually groups settings.","effect":"This element has no effect in terms of clicking.","rivals":[]},"background ":"Tidwell \u2018Designing Interfaces\u2019 proposed the pattern of \u2018Titled Sections\u2019 and describes it as: \u201cDefine separate sections of content by giving each one a visually strong title, separating the sections visually, \u2026 \u201c. She further explains that those sections should be grouped by topic or task. According to Tidwell is content that is sectioned neatly into chunks easier to grasp since the human visual system is always looking for bigger patterns. In ILIAS such sections in a form are called titled form sections.","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":{"1":"Proper Titled Form Sections SHOULD comprise 2 to 5 Settings.","2":"More than 5 Settings SHOULD be split into two areas unless this would tamper with the \u201cfamiliar\u201d information architecture of forms.","3":"There MUST NOT be a Setting without an enclosing Titled Form Section. If necessary a Titled Form Section MAY contain only one single Setting.","4":"The first and last titled form section of a form MUST contain a \u201cSave\u201d and \u201cCancel\u201d button for the form. \u201cSave\u201d is left and \u201cCancel\u201d is right.","5":"In some rare exceptions the Buttons MAY be named differently: if \u201cSave\u201d or \u201cCancel\u201d are clearly a misleading since the action is more than storing the data into the database. \u201cSend Mail\u201d would be an example of this."},"interaction":[],"wording":{"1":"A Titled Form Section MUST contain a title.","2":"The title SHOULD summarize the contained settings accurately from a user\u2019s perspective.","3":"The title SHOULD contain less than 30 characters.","4":"The titles MUST be cross-checked with similar forms in other objects or services to ensure consistency throughout ILIAS.","5":"In doubt consistency SHOULD be prioritized over accuracy in titles."},"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"InputContainerFormFactoryForm","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Container\/Form\/Section"},"InputContainerFormSubSub":{"id":"InputContainerFormSubSub","title":"Sub","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Settings can be nested allowing for settings-dependent further configuration.","composition":"Subsettings are underneath the Setting they depend on. The Subsetting is indented to visually confirm said dependence.","effect":"Subsettings are revealed after enabling the selection control of a setting. If the setting is not enabled, the subsetting remains hidden.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"There MUST NOT be a nesting of more than one subsetting (see Jour Fixe comment in feature wiki reference). The only exception to this rule is the required quantification of a subsetting by a date or number. These exceptions MUST individually accepted by the Jour Fixe.","2":"The title SHOULD summarize the contained settings accurately from a user\u2019s perspective.","3":"The title SHOULD contain less than 30 characters.","4":"The titles MUST be cross-checked with similar forms in other objects or services to ensure consistency throughout ILIAS.","5":"In doubt consistency SHOULD be prioritized over accuracy in titles."},"composition":{"1":"Subsettings MUST bear an identifier."},"interaction":{"1":"Subsetting MUST NOT be enabled by any other form element than a checkbox or a radio input group."},"wording":[],"ordering":{"1":"Subsettings of a setting can be stacked. The most relevant subsetting MUST be the first subsetting in the stack. The least relevant comes last in the stack."},"style":[],"responsiveness":[],"accessibility":[]},"parent":"InputContainerFormFactoryForm","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Container\/Form\/Sub"},"InputItemSelectorFactorySelector":{"id":"InputItemSelectorFactorySelector","title":"Selector","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A selector allows to chose from a finite set of elements. There are 1:n selectors, which allow to select exactly one out of n elements (this is sometimes also called single choice) and m:n Selections to select m elements of of a set of n elements, where n<=m (multiple choice).","composition":"Selection Controls consist of selectable elements in the form of e.g. Checkboxes, Radio Options and Selection Lists.","effect":"","rivals":[]},"background ":"Tiddwell describes Selection Controls as \u201cList of Items\u201d and states that the choice of control \u201cdepends on the number of items or options to be selected (one or many) and the number of potentially selectable items (two, a handful, or many)\u201d.","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"Toggling the selection of a focused element MUST be achievable by only using the keyboard."}},"parent":"InputItemFactoryItem","children":["InputItemSelectorRepositoryRepository","InputItemSelectorRadioGroupRadioGroup","InputItemSelectorRadioGroupRadioOption"],"less_variables":[],"path":"src\/UI\/Component\/Input\/Item\/Selector\/Factory"},"InputItemFieldFactoryField":{"id":"InputItemFieldFactoryField","title":"Field","abstract":1,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Text Input Field are used to enter characters into one or mulitple Input Fields.","composition":"They consist of one or multiple Input Fields.","effect":"They allow the enter characters by keyboard.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":[],"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":{"1":"All Input Fields MUST be accessible by keyboard."}},"parent":"InputItemFactoryItem","children":["InputItemFieldTextText"],"less_variables":[],"path":"src\/UI\/Component\/Input\/Item\/Field\/Factory"},"InputItemSelectorRepositoryRepository":{"id":"InputItemSelectorRepositoryRepository","title":"Repository","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Repository Selectors pick a specific repository object to e.g. limit its application, to point to it, to become a starting point or the like. They can pick exactly one repository object or multiple.","composition":"Repository selectors contain of two main parts. One part shows the current selection with a triggers to change this selction by opening part two, a modal containing the options in a tree to make a new selection from.","effect":"The interaction slightly difers from selecting one to selecing multiple objects. <\/br>-> If one object is selected, the user clicks the \"Select\"-Link in the form. ILIAS calls a Roundtrip Modal presenting the repository tree. User selects then the object by clicking on an object title (to pick one object). ILIAS closes the modal and inserts the selected object into the form. <\/br>-> If one multiple objects are to be selected, then the user also clicks the \"Select\"-Link in the form. ILIAS calls a Roundtrip Modal presenting the repository tree. User checks all checkboxes to pick more than one object and has to click \"Save\" in order to complete the selction. ILIAS closes the modal and inserts the selected object into the form and adds Add\/Remove-Glyphs.","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Repository Pickers MAY be used in Forms."},"composition":[],"interaction":[],"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"InputItemSelectorFactorySelector","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Item\/Selector\/Repository"},"InputItemSelectorRadioGroupRadioGroup":{"id":"InputItemSelectorRadioGroupRadioGroup","title":"Radio Group","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A radio group input allows for choosing between mutually exclusive but related options.","composition":"he radio group has one identifier stating the common denominator of the mutually exclusive options.","effect":"Options of a radio group may open a sub form.","rivals":{"Select":"Select Inputs are used if a selection of more than 5 items has to be displayed."}},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"A radio group SHOULD contain 3 to 5 options. They MAY also be used to select between two options where one is not automatically the inverse of the other (such as \u201cOrder by Date\u201d and \u201cOrder by Name\u201d).","2":"If more than 5 options are available a Select Input SHOULD be used."},"composition":[],"interaction":[],"wording":{"1":"Each option in a radio group MUST be labeled. This label SHOULD not consist of more than 5 words. Simple language in the labels is to be used. Technical terms should be avoided whenever possible or relegated to the by-line.","2":"If used in forms, the label of the options SHOULD not simply repeat the identifier on the left. A meaningful labeling SHOULD be chosen instead."},"ordering":{"1":"The presumably most relevant option SHOULD be the first option.","2":"Potentially damaging options SHOULD be listed last."},"style":[],"responsiveness":[],"accessibility":[]},"parent":"InputItemSelectorFactorySelector","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Item\/Selector\/RadioGroup"},"InputItemSelectorRadioGroupRadioOption":{"id":"InputItemSelectorRadioGroupRadioOption","title":"Radio Option","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Todo","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"InputItemSelectorFactorySelector","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Item\/Selector\/RadioGroup"},"InputItemFieldTextText":{"id":"InputItemFieldTextText","title":"Text","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"A Text Field Input field captures a one-line input of text .","composition":"This default text input field is restricted to one line of text.","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":{"usage":{"1":"Text Field Inputs MUST NOT be used for choosing from predetermined options choices.","2":"Text Field Inputs SHOULD only be used when any kind of input can be entered.","3":"Text Field Inputs MUST NOT be used for numeric input, a Number Field Input is to be used instead.","4":"Text Field Inputs MUST NOT be used for letter-only input, an Alphabet Field Input is to be used instead."},"composition":[],"interaction":{"1":"Text Field Inputs MUST have a limited the number of accepted characters to be accepted if only a limited number of characters is to be stored to the database.","2":"Text Field Inputs MUST NOT provide the default value in the description because it is at high risk to be missed."},"wording":[],"ordering":[],"style":[],"responsiveness":[],"accessibility":[]},"parent":"InputItemFieldFactoryField","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Item\/Field\/Text"},"InputValidationNotEmptyNotEmpty":{"id":"InputValidationNotEmptyNotEmpty","title":"Not Empty","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Not empty validators check whether an input has content.","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"InputValidationFactoryValidation","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Validation\/NotEmpty"},"InputValidationRegexEquals":{"id":"InputValidationRegexEquals","title":"Equals","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"The equals validator checks wheter the input equals a certain value.","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"InputValidationFactoryValidation","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Validation\/Regex"},"InputValidationRegexRegex":{"id":"InputValidationRegexRegex","title":"Regex","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Regex validators validate content by checking if it complies with a given regular expression.","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"InputValidationFactoryValidation","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Validation\/Regex"},"InputValidationCustomCustom":{"id":"InputValidationCustomCustom","title":"Custom","abstract":0,"status_entry":"Proposed","status_implementation":"Partly implemented","description":{"purpose":"Custom validators accept a user defined function for validating content.","composition":"","effect":"","rivals":[]},"background ":"","selector":"","feature_wiki_references ":[],"rules":null,"parent":"InputValidationFactoryValidation","children":[],"less_variables":[],"path":"src\/UI\/Component\/Input\/Validation\/Custom"}} \ No newline at end of file diff --git a/src/UI/Component/Input/Container/Container.php b/src/UI/Component/Input/Container/Container.php new file mode 100755 index 000000000000..6ad44c054f50 --- /dev/null +++ b/src/UI/Component/Input/Container/Container.php @@ -0,0 +1,10 @@ + + * Filters enable to add and remove criteria for displaying or hiding items in a list or table. + * composition: > + * Filters are composed of the input selectors and fields for composing the individual criteria for the filter along with an “Apply Filter” Button to apply Filter Button. + * + * rules: + * accessibility: + * 1: Every Input Element in every Input Collection MUST be accessible by keyboard. + * + * ---- + * @return \ILIAS\UI\Component\Container\Filter\Filter + */ + public function filter(); + + /** + * --- + * description: + * purpose: > + * Forms are used for creating content of sub-items or for configuring objects or services. + * composition: > + * A form is divided into areas, which are further divided into sections containing settings with selection or input field controls. + * effect: > + * Users manipulate input in settings and save the form to apply the settings to the object or service. + * + * context: > + * Forms are usually placed in a tabbed content area. + * + * rules: + * usage: + * 1: Forms MUST NOT be used on the same content screen as tables. + * 2: Forms MUST NOT be used on the same content screen as toolbars. + * composition: + * 1: Each form MUST contain at least one titled form section. + * + * ---- + * @return \ILIAS\UI\Component\Input\Container\Form\Factory + */ + public function form(); + +} \ No newline at end of file diff --git a/src/UI/Component/Input/Container/Filter/Filter.php b/src/UI/Component/Input/Container/Filter/Filter.php new file mode 100755 index 000000000000..b8e0a9052dbb --- /dev/null +++ b/src/UI/Component/Input/Container/Filter/Filter.php @@ -0,0 +1,9 @@ + + * Currently the only form available in ILIAS + * + * ---- + * @param string + * @param \ILIAS\UI\Component\Input\Item[] + * @return \ILIAS\UI\Component\Input\Container\Form\Standard + */ + public function standard($action = "#", $title, $items); + + /** + * --- + * description: + * purpose: > + * A titled form section subdivides a form allowing users to browse the screen. + * Titled Form Sections are stacked. The stacking reflects perceived relevance + * and is kept consistent across objects thus learners are “familiar” with forms. + * composition: > + * At least one titled form section is used in every form. + * A title states the purpose shared by all settings of the titled form section. + * The section visually groups settings. + * effect: > + * This element has no effect in terms of clicking. + * + * background: > + * Tidwell ‘Designing Interfaces’ proposed the pattern of + * ‘Titled Sections’ and describes it as: “Define separate sections of content by giving each one a visually strong title, + * separating the sections visually, … “. She further explains that those sections should be grouped by topic or task. + * According to Tidwell is content that is sectioned neatly into chunks easier to grasp since the human + * visual system is always looking for bigger patterns. In ILIAS such sections in a form are called titled form sections. + * + * rules: + * wording: + * 1: A Titled Form Section MUST contain a title. + * 2: The title SHOULD summarize the contained settings accurately from a user’s perspective. + * 3: The title SHOULD contain less than 30 characters. + * 4: The titles MUST be cross-checked with similar forms in other objects or services to ensure consistency throughout ILIAS. + * 5: In doubt consistency SHOULD be prioritized over accuracy in titles. + * composition: + * 1: Proper Titled Form Sections SHOULD comprise 2 to 5 Settings. + * 2: More than 5 Settings SHOULD be split into two areas unless this would tamper with the “familiar” information architecture of forms. + * 3: There MUST NOT be a Setting without an enclosing Titled Form Section. If necessary a Titled Form Section MAY contain only one single Setting. + * 4: The first and last titled form section of a form MUST contain a “Save” and “Cancel” button for the form. “Save” is left and “Cancel” is right. + * 5: > + * In some rare exceptions the Buttons MAY be named differently: if “Save” or “Cancel” are clearly a + * misleading since the action is more than storing the data into the database. “Send Mail” would be an example of this. + * + * ---- + * @return \ILIAS\UI\Component\Input\Container\Form\Section + */ + public function section($title, $items); + + /** + * --- + * description: + * purpose: > + * Settings can be nested allowing for settings-dependent further configuration. + * composition: > + * Subsettings are underneath the Setting they depend on. The Subsetting is indented to visually confirm said dependence. + * effect: > + * Subsettings are revealed after enabling the selection control of a setting. If the setting is not enabled, the subsetting remains hidden. + * + * rules: + * usage: + * 1: > + * There MUST NOT be a nesting of more than one subsetting (see Jour Fixe comment in feature wiki reference). + * The only exception to this rule is the required quantification of a subsetting by a date or number. + * These exceptions MUST individually accepted by the Jour Fixe. + * 2: The title SHOULD summarize the contained settings accurately from a user’s perspective. + * 3: The title SHOULD contain less than 30 characters. + * 4: The titles MUST be cross-checked with similar forms in other objects or services to ensure consistency throughout ILIAS. + * 5: In doubt consistency SHOULD be prioritized over accuracy in titles. + * composition: + * 1: Subsettings MUST bear an identifier. + * interaction: + * 1: Subsetting MUST NOT be enabled by any other form element than a checkbox or a radio input group. + * ordering: + * 1: > + * Subsettings of a setting can be stacked. The most relevant subsetting MUST be the first subsetting in the stack. + * The least relevant comes last in the stack. + * + * ---- + * @return \ILIAS\UI\Component\Input\Container\Form\Sub + */ + public function sub($items); +} \ No newline at end of file diff --git a/src/UI/Component/Input/Container/Form/Section.php b/src/UI/Component/Input/Container/Form/Section.php new file mode 100755 index 000000000000..86d9c5f2834e --- /dev/null +++ b/src/UI/Component/Input/Container/Form/Section.php @@ -0,0 +1,9 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Component\Input\Container\Form; + +interface Section extends \ILIAS\UI\Component\Input\Container\Container { + /** + * @Todo: Add Interface + */ +} \ No newline at end of file diff --git a/src/UI/Component/Input/Factory.php b/src/UI/Component/Input/Factory.php new file mode 100755 index 000000000000..d8af0e324c65 --- /dev/null +++ b/src/UI/Component/Input/Factory.php @@ -0,0 +1,104 @@ + + * Input Containers are aggregate controls in order to gather input from the user. Examples are Forms or Filters. + * composition: > + * They consist of one or multiple Input items. + * effect: > + * Input entered into in Input Collection is saved by clicking on an Interaction Trigger, mostly a Button. + * + * rules: + * accessibility: + * 1: Every Input Item in every Input Collection MUST be accessible by keyboard. + * + * ---- + * @return \ILIAS\UI\Component\Input\Container\Factory + */ + public function container(); + + /** + * --- + * description: + * purpose: > + * An Input Item is used to collect one specific piece of information from the user. + * composition: > + * A Input item is composed of an label naming the concrete item, might contain a by line describing it and an input control + * the collect the actual data from the user. + * effect: > + * The effect depends on the control-type of the input element. + * + * rules: + * composition: + * 1: A Input Item MUST use a label. + * wording: + * 1: > + * A label MUST be composed of one single term or a very short phrase. + * The label is an eye catcher for users skimming the form. + * 2: > + * A label MUST avoid lingo. Intelligibility by occasional users is prioritized over technical accuracy. + * The accurate technical expression is to be mentioned in the by-line. + * 3: > + * An label MUST make a positive statement. + * If the purpose of the setting is inherently negative, use Verbs as “Limit..”, “Lock..”. + * 4: > + * Wording and structure (sequence and control) MUST be consistent with identifiers in other objects. + * If you feel a wording needs to be changed, then you MUST propose it to the JF. + * 5: A by-line (explanatory text) MAY be added below the input element. + * 6: > + * If by-lines are provided they MUST be informative, not merely repeating the identifier’s or input element’s content. + * If no informative description can be devised, no description is needed. + * 7: > + * A by-line MUST clearly state what effect the Setting produces and explain, why this might be important and what it can be used for. + * 8: > + * Bulk by-lines underneath a stack of option explaining all of the options in one paragraph MUST NOT be used. + * 9: > + * A by-line SHOULD NOT address the user directly. Addressing users directly is reserved for cases of high risk of severe mis-configuration. + * 10: > + * A by-line MUST be grammatically complete sentence with a period (.) at the end. + * 11: > + * By-lines SHOULD be short with no more than 25 words. + * 12: > + * By-lines SHOULD NOT use any formatting in descriptions (bold, italic or similar). + * 13: > + * If by-lines refer to other tabs or options or tables by name, that reference should be made in quotation marks: + * ‘Info’-tab / "Info"-Reiter, button ‘Show Test Results’ / Button "Testergebnisse anzeigen", + * ‘Table of Detailed Test Results’ / "Tabelle mit detaillierten Testergebnissen". + * Use proper quotation marks, not apostrophs. + * Use single quotation marks for english language and double quotation maks for german language. + * 14: > + * By-lines MUST NOT feature parentheses since they greatly diminish readability. + * 15: > + * By-lines SHOULD NOT start with terms such as: If this option is set … If this setting is active … + * Choose this setting if … This setting … Rather state what happens directly: Participants get / make / can … + * Point in time after which…. ILIAS will monitor… Sub-items xy are automatically whatever .. Xy will be displayed at place. + * + * + * + * ---- + * @return \ILIAS\UI\Component\Input\Item\Factory + */ + public function item(); + + /** + * --- + * description: + * purpose: > + * Validation components are used to validate various kinds of inputs. + * composition: > + * Validation components are usually placed inside of other inputs components. They can not work on their own. + * effect: > + * Validations show whether an input is (or will be) accepted by the system or is (or was) rejected. + * + * ---- + * @return \ILIAS\UI\Component\Input\Validation\Factory + */ + public function validation(); + +} \ No newline at end of file diff --git a/src/UI/Component/Input/Input.php b/src/UI/Component/Input/Input.php new file mode 100644 index 000000000000..0ee252dc5cfc --- /dev/null +++ b/src/UI/Component/Input/Input.php @@ -0,0 +1,155 @@ +addMapping( + * function($input){ + * return ["item 1"=>$input[0],"item 2"=>$input[1]]; + * }); + * + * Example 2, directly store into objects: + * $structured_output = new stdClass(); + * $input = $input->addMapping( + * function($input){ + * $structured_output->item1 = $input[0]; + * $structured_output->item2 = $input[1]; + * }); + * + * Example 3, mapping of example 2 chained to mapping of example 1: + * $input = $input->addMapping( + * function($input){ + * return ["item 1"=>$input[0],"item 2"=>$input[1]]; + * }); + * $structured_output = new stdClass(); + * $input = $input->addMapping( + * function($input){ + * $structured_output->item1 = $input["item 1"]; + * $structured_output->item2 = $input["item 2"]; + * }); + * + * @param callable $function + * @return Input + */ + public function addMapping(Callable $function); + + /** + * Apply the added mappings and return the value return by them. + * + * @return mixed + */ + public function map(); + + /** + * Validations are objects capsuling a function to perform validations. In most cases + * it should be sufficient to use one of the standard validations available in the + * framework. However it is also possible to use custom validations or to create new + * ones to be reused. Validations may be combined. In such a case, all combined + * validations must passed for a given input. + * + * Example 1, using a standard validation checking if input equals 3: + * $input = $input->addValidation($factory->input()->validation()->equals(3)); + * + * Example 2, using a custom validation checking if input is smaller than 3: + * $input = $input->addValidation( + * $f->input()->validation()->custom( + * function ($input){ + * return $input < 3; + * },"Not smaller than 3")); + * + * + * @param V\Validation $validation + */ + public function addValidation(V\Validation $validation); + + /** + * Create a new input with values set from a nested array (mostly from POST). + * This input will automatically be validated and passed on to children of this + * element if available. + * + * @param [] $input + * @return $this + */ + public function withInputFromView($input); + + /** + * Get if the input of this input and it's children is valid + * @return bool + */ + public function isValid(); + + /** + * Get if the input has been validated (it it has been set by withInputFromView) + * @return bool + */ + public function isValidated(); + + /** + * The message collector holds all messages set by failed validations along with a + * reference to the inputs that failed the validation + * + * @return V\ValidationMessageCollector + */ + public function getMessageCollector(); + + + /** + * Create a new Input with some value. This will not be validated. Use this to set + * the input from some model. + * + * @param mixed $value + * @return Input + */ + public function withValue($value); + + /** + * Get the value of the input. + * + * @return mixed + */ + public function getValue(); + + /** + * Create an instance of the input with a set of children. + * @param Input[] $children + * @return Input + */ + public function withChildren($children); + + /** + * Get the children of the input. + * + * @return Input[] + */ + public function getChildren(); + + /** + * Get whether the input has children. + * + * @return bool + */ + public function hasChildren(); +} \ No newline at end of file diff --git a/src/UI/Component/Input/Item/Factory.php b/src/UI/Component/Input/Item/Factory.php new file mode 100755 index 000000000000..8af527c0dfde --- /dev/null +++ b/src/UI/Component/Input/Item/Factory.php @@ -0,0 +1,49 @@ + + * A selector allows to chose from a finite set of elements. There are 1:n selectors, which allow to select exactly one out of n elements + * (this is sometimes also called single choice) and m:n Selections to select m elements of of a set of n elements, where n<=m (multiple choice). + * composition: > + * Selection Controls consist of selectable elements in the form of e.g. Checkboxes, Radio Options and Selection Lists. + * effect: > + * + * background: > + * Tiddwell describes Selection Controls as “List of Items” and states that the choice of control + * “depends on the number of items or options to be selected (one or many) and the number of potentially selectable items (two, a handful, or many)”. + * + * rules: + * accessibility: + * 1: Toggling the selection of a focused element MUST be achievable by only using the keyboard. + * + * ---- + * @return \ILIAS\UI\Component\Input\Item\Selector\Factory + */ + public function selector(); + + /** + * --- + * description: + * purpose: > + * Text Input Field are used to enter characters into one or mulitple Input Fields. + * composition: > + * They consist of one or multiple Input Fields. + * effect: > + * They allow the enter characters by keyboard. + * + * rules: + * accessibility: + * 1: All Input Fields MUST be accessible by keyboard. + * + * ---- + * @return \ILIAS\UI\Component\Input\Item\Field\Factory + */ + public function field(); + +} \ No newline at end of file diff --git a/src/UI/Component/Input/Item/Field/Factory.php b/src/UI/Component/Input/Item/Field/Factory.php new file mode 100755 index 000000000000..32a7e6938166 --- /dev/null +++ b/src/UI/Component/Input/Item/Field/Factory.php @@ -0,0 +1,31 @@ + + * A Text Field Input field captures a one-line input of text . + * composition: > + * This default text input field is restricted to one line of text. + * + * rules: + * usage: + * 1: Text Field Inputs MUST NOT be used for choosing from predetermined options choices. + * 2: Text Field Inputs SHOULD only be used when any kind of input can be entered. + * 3: Text Field Inputs MUST NOT be used for numeric input, a Number Field Input is to be used instead. + * 4: Text Field Inputs MUST NOT be used for letter-only input, an Alphabet Field Input is to be used instead. + * interaction: + * 1: Text Field Inputs MUST have a limited the number of accepted characters to be accepted if only a limited number of characters is to be stored to the database. + * 2: Text Field Inputs MUST NOT provide the default value in the description because it is at high risk to be missed. + * + * ---- + * @return \ILIAS\UI\Component\Input\Item\Field\Text + */ + public function text($label); +} \ No newline at end of file diff --git a/src/UI/Component/Input/Item/Field/Text.php b/src/UI/Component/Input/Item/Field/Text.php new file mode 100755 index 000000000000..0aaa6b3d1c0f --- /dev/null +++ b/src/UI/Component/Input/Item/Field/Text.php @@ -0,0 +1,16 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Component\Input\Item\Field; + +/** + * + * Interface Selector + * @package ILIAS\UI\Component\Selector + */ +interface Text extends \ILIAS\UI\Component\Input\Item\Item { + /** + * @Todo: Add Interface + */ +} \ No newline at end of file diff --git a/src/UI/Component/Input/Item/Item.php b/src/UI/Component/Input/Item/Item.php new file mode 100755 index 000000000000..b7d466d3b0c9 --- /dev/null +++ b/src/UI/Component/Input/Item/Item.php @@ -0,0 +1,32 @@ + + * Repository Selectors pick a specific repository object to e.g. limit its application, to point to it, to become a starting point or the like. + * They can pick exactly one repository object or multiple. + * composition: > + * Repository selectors contain of two main parts. One part shows the current selection with a triggers to change + * this selction by opening part two, a modal containing the options in a tree to make a new selection from. + * effect: > + * The interaction slightly difers from selecting one to selecing multiple objects. + *
-> If one object is selected, the user clicks the "Select"-Link in the form. ILIAS calls a Roundtrip + * Modal presenting the repository tree. User selects then the object by clicking on an object title (to pick one object). + * ILIAS closes the modal and inserts the selected object into the form. + *
-> If one multiple objects are to be selected, then the user also clicks the "Select"-Link in the form. ILIAS calls a Roundtrip + * Modal presenting the repository tree. User checks all checkboxes to pick more than one object and has to click + * "Save" in order to complete the selction. ILIAS closes the modal and inserts the selected object into the form and adds + * Add/Remove-Glyphs. + * + * rules: + * usage: + * 1: > + * Repository Pickers MAY be used in Forms. + * + * ---- + * @param @Todo define structure to pass tree + * @return \ILIAS\UI\Component\Input\Item\Selector\Repository + */ + public function repository(); + + + + /** + * --- + * description: + * purpose: > + * A radio group input allows for choosing between mutually exclusive but related options. + * composition: > + * he radio group has one identifier stating the common + * denominator of the mutually exclusive options. + * effect: > + * Options of a radio group may open a sub form. + * rivals: + * Select: Select Inputs are used if a selection of more than 5 items has to be displayed. + * + * rules: + * usage: + * 1: > + * A radio group SHOULD contain 3 to 5 options. They MAY also be used to + * select between two options where one is not automatically the inverse of the + * other (such as “Order by Date” and “Order by Name”). + * 2: If more than 5 options are available a Select Input SHOULD be used. + * wording: + * 1: > + * Each option in a radio group MUST be labeled. This label SHOULD not consist of more than 5 + * words. Simple language in the labels is to be used. Technical terms should be avoided + * whenever possible or relegated to the by-line. + * 2: > + * If used in forms, the label of the options SHOULD not simply repeat the identifier on the left. + * A meaningful labeling SHOULD be chosen instead. + * ordering: + * 1: The presumably most relevant option SHOULD be the first option. + * 2: Potentially damaging options SHOULD be listed last. + * + * ---- + * @param RadioOption[] $radio_options Radio options to be offered by the radio Group + * @return \ILIAS\UI\Component\Input\Item\Selector\RadioGroup + */ + public function radioGroup($id,$label,$radio_options); + + /** + * --- + * description: + * purpose: > + * Todo + * + * ---- + * @param RadioOption[] $radio_options Radio options to be offered by the radio Group + * @return \ILIAS\UI\Component\Input\Item\Selector\RadioGroup + */ + public function radioOption($id,$label); +} \ No newline at end of file diff --git a/src/UI/Component/Input/Item/Selector/RadioGroup.php b/src/UI/Component/Input/Item/Selector/RadioGroup.php new file mode 100755 index 000000000000..4520341f4175 --- /dev/null +++ b/src/UI/Component/Input/Item/Selector/RadioGroup.php @@ -0,0 +1,14 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Component\Input\Item\Selector; + +/** + * Interface RadioGroup + * @package ILIAS\UI\Component\Input\Item\Selector\Factory + */ +interface RadioGroup extends \ILIAS\UI\Component\Input\Item\Item { + /** + * @Todo: Add Interface + */ +} \ No newline at end of file diff --git a/src/UI/Component/Input/Item/Selector/RadioOption.php b/src/UI/Component/Input/Item/Selector/RadioOption.php new file mode 100755 index 000000000000..8255bf134b17 --- /dev/null +++ b/src/UI/Component/Input/Item/Selector/RadioOption.php @@ -0,0 +1,12 @@ + + * Not empty validators check whether an input has content. + * ---- + * @return \ILIAS\UI\Component\Input\Validation\NotEmpty + */ + public function notEmpty(); + + /** + * --- + * description: + * purpose: > + * The equals validator checks wheter the input equals a certain value. + * ---- + * @param mixed $to_be_equaled + * @return \ILIAS\UI\Component\Input\Validation\Regex + */ + public function equals($to_be_equaled); + + /** + * --- + * description: + * purpose: > + * Regex validators validate content by checking if it complies with a given regular expression. + * + * ---- + * @param string $regex + * @return \ILIAS\UI\Component\Input\Validation\Regex + */ + public function regex($regex); + + /** + * --- + * description: + * purpose: > + * Custom validators accept a user defined function for validating content. + * + * ---- + * @param callable $validation + * @param string $message + * @return \ILIAS\UI\Component\Input\Validation\Custom + */ + public function custom(callable $validation, $message); +} \ No newline at end of file diff --git a/src/UI/Component/Input/Validation/NotEmpty.php b/src/UI/Component/Input/Validation/NotEmpty.php new file mode 100755 index 000000000000..ffb1cb36274c --- /dev/null +++ b/src/UI/Component/Input/Validation/NotEmpty.php @@ -0,0 +1,11 @@ + + * Inputs capture data from users. Some Inputs capture well-structured data i.e. users choose from a set of options. + * Other Inputs capture data that is actively produced by users i.e. text or file uploads. + * composition: > + * The composition of the inputs heavily depend on the exact type. + * effect: > + * Entering input has per default no effect. E.g. in Forms, input has to be applied by clicking a “Save”-button to become effective. + * Thus Inputs are mostly accompanied by a button or contain one themselves. + * + * rules: + * style: + * 1: Disabled Input Elements MUST be indicated by setting the “disabled” attribute. + * 2: If focused, the Input Elements MUST change their Input Border Color to the Input Focus Border Color. + * accessibility: + * 1: All Input Elements visible in a view MUST be accessible by keyboard by using the ‘Tab’-Key. + * --- + * @return \ILIAS\UI\Component\Input\Factory + */ + public function input(); + } diff --git a/src/UI/Implementation/Component/Input/Container/Container.php b/src/UI/Implementation/Component/Input/Container/Container.php new file mode 100755 index 000000000000..6fa3dae218a4 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Container/Container.php @@ -0,0 +1,20 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Container; + +use ILIAS\UI\Component\Input\Container as C; +use ILIAS\UI\Implementation\Component\ComponentHelper; + +/** + * Class Factory + */ +class Factory implements C\Factory { + + use ComponentHelper; + + /** + * @inheritdoc + */ + public function filter() { + } + + /** + * @inheritdoc + */ + public function form() { + return new Form\Factory(); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Container/Form/Factory.php b/src/UI/Implementation/Component/Input/Container/Form/Factory.php new file mode 100755 index 000000000000..ec072f55f72a --- /dev/null +++ b/src/UI/Implementation/Component/Input/Container/Form/Factory.php @@ -0,0 +1,37 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Container\Form; + +use ILIAS\UI\Component\Input\Container\Form as F; +use ILIAS\UI\Implementation\Component\ComponentHelper; + +/** + * Class Factory + */ +class Factory implements F\Factory { + + use ComponentHelper; + + /** + * @inheritdoc + */ + public function standard($action = "#", $title, $items) { + return new Standard($action, $title, $items); + } + + /** + * @inheritdoc + */ + public function section($title, $items) { + return new Section($title, $items); + } + + /** + * @inheritdoc + */ + public function sub($items) { + return new Sub($items); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Container/Form/Renderer.php b/src/UI/Implementation/Component/Input/Container/Form/Renderer.php new file mode 100755 index 000000000000..da9f9716d159 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Container/Form/Renderer.php @@ -0,0 +1,86 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Container\Form; + +use ILIAS\UI\Implementation\Render\AbstractComponentRenderer; +use ILIAS\UI\Renderer as RendererInterface; +use ILIAS\UI\Component; + +/** + * Class Renderer + * Todo this is mostly experimenting + * @package ILIAS\UI\Implementation\Component\Image + */ +class Renderer extends AbstractComponentRenderer { + /** + * @inheritdocs + */ + public function render(Component\Component $component, RendererInterface $default_renderer) { + /** + * @var Component\Input\Container\Container $component + */ + $this->checkComponent($component); + $tpl = null; + + if($component instanceof Standard){ + $default_content = ""; + $sections = ""; + + $tpl = $this->getTemplate("Form/tpl.standard.html", true, true); + + + foreach($component->extractToView() as $item){ + if($item instanceof Section){ + $sections .= $default_renderer->render($item); + }else { + if (!($item instanceof Component\Component)) { + var_dump($component->extractToView()); + exit; + + } + + $default_content .= $default_renderer->render($item); + } + } + + $tpl->setVariable("CONTENT_DEFAULT_SECTION",$default_content); + $tpl->setVariable("ADDITIONAL_SECTIONS",$sections); + $tpl->setVariable("ACTION",$component->getAction()); + $tpl->setVariable("FORM_TITLE",$component->getTitle()); + + + }else{ + if($component instanceof SECTION) + { + $tpl = $this->getTemplate("Form/tpl.section.html", true, true); + $tpl->setVariable("SECTION_TITLE", $component->getTitle()); + }else + { + $tpl = $this->getTemplate("Form/tpl.sub.html", true, true); + } + + + $content = ""; + foreach($component->extractToView() as $item){ + $content .= $default_renderer->render($item); + } + + + $tpl->setVariable("CONTENT",$content); + + } + + return $tpl->get(); + + + } + + /** + * @inheritdocs + */ + protected function getComponentInterfaceName() { + return [Component\Input\Container\Container::class]; + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Container/Form/Section.php b/src/UI/Implementation/Component/Input/Container/Form/Section.php new file mode 100755 index 000000000000..a90ed3f7482d --- /dev/null +++ b/src/UI/Implementation/Component/Input/Container/Form/Section.php @@ -0,0 +1,32 @@ +title = $title; + parent::__construct($title,$items); + } + + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } +} diff --git a/src/UI/Implementation/Component/Input/Container/Form/Standard.php b/src/UI/Implementation/Component/Input/Container/Form/Standard.php new file mode 100755 index 000000000000..23f8307e2e24 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Container/Form/Standard.php @@ -0,0 +1,56 @@ +action = $action; + $this->title = $title; + + parent::__construct($items); + } + + /** + * @inheritdocs + */ + public function getAction(){ + return $this->action; + } + + /** + * @inheritdocs + */ + public function getTitle() + { + return $this->title; + } +} diff --git a/src/UI/Implementation/Component/Input/Container/Form/Sub.php b/src/UI/Implementation/Component/Input/Container/Form/Sub.php new file mode 100755 index 000000000000..bb6a7bb55d47 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Container/Form/Sub.php @@ -0,0 +1,22 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input; + +use ILIAS\UI\Component\Input as I; +use ILIAS\UI\Implementation\Component\ComponentHelper; + +/** + * Class Factory + * + * @package ILIAS\UI\Implementation\Component\Filter + */ +class Factory implements I\Factory { + + use ComponentHelper; + + /** + * @inheritdoc + */ + public function container() { + return new Container\Factory(); + } + /** + * @inheritdoc + */ + public function item() { + return new Item\Factory(); + } + /** + * @inheritdoc + */ + public function validation() { + return new Validation\Factory(); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Formlet/Exception/Apply.php b/src/UI/Implementation/Component/Input/Formlet/Exception/Apply.php new file mode 100644 index 000000000000..d57c40b40a32 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/Exception/Apply.php @@ -0,0 +1,17 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code. + * + */ +namespace ILIAS\UI\Implementation\Component\Input\Formlet\Internal\Exception; +use Exception; + +class Apply extends Exception { + public function __construct($what, $other) { + parent::__construct("Can't apply $what to $other"); + } +} diff --git a/src/UI/Implementation/Component/Input/Formlet/Exception/Get.php b/src/UI/Implementation/Component/Input/Formlet/Exception/Get.php new file mode 100644 index 000000000000..29a1553bf712 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/Exception/Get.php @@ -0,0 +1,17 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code. + * + */ +namespace ILIAS\UI\Implementation\Component\Input\Formlet\Internal\Exception; +use Exception; + +class Get extends Exception { + public function __construct($what) { + parent::__construct("Can't get value from $what"); + } +} diff --git a/src/UI/Implementation/Component/Input/Formlet/Exception/MissingInput.php b/src/UI/Implementation/Component/Input/Formlet/Exception/MissingInput.php new file mode 100644 index 000000000000..ee8c218c128b --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/Exception/MissingInput.php @@ -0,0 +1,23 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code. + */ +namespace ILIAS\UI\Implementation\Component\Input\Formlet\Internal\Exception; + +use Exception; + +/** + * Class MissingInput + * @package ILIAS\UI\Component\Implementation\Input\Formlet\Internal\Exception + */ +class MissingInput extends Exception { + private $_name; //string + public function __construct($name) { + $this->_name = $name; + parent::__construct("Missing input $name."); + } +} diff --git a/src/UI/Implementation/Component/Input/Formlet/Factory.php b/src/UI/Implementation/Component/Input/Formlet/Factory.php new file mode 100644 index 000000000000..4214d6379ad9 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/Factory.php @@ -0,0 +1,24 @@ + Extended GPL, see docs/LICENSE */ +namespace ILIAS\UI\Implementation\Component\Input\Formlet; + + +/** + * Class Factory + * + */ +class Factory { + /** + * Todo: This static function is poisson for testability and proper DI + */ + public static function getFactory(){ + return new self(); + } + + + public function formlet($name) { + return new Formlet($name); + } + +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Formlet/Formlet.php b/src/UI/Implementation/Component/Input/Formlet/Formlet.php new file mode 100644 index 000000000000..8efea3f1eea1 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/Formlet.php @@ -0,0 +1,355 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code." + * + * See: https://github.com/lechimp-p/php-formlets + */ +/* Copyright (c) 2016 Timon Amstutz Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Formlet; + +use \ILIAS\UI\Component\Input\Input; +use \ILIAS\UI\Component\Input\Validation\Validation; +use \ILIAS\UI\Implementation\Component\Input\Validation\Validations; +use \ILIAS\UI\Implementation\Component\Input\Validation\ValidationMessageCollector; +use \ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue as FV; + + +/** + * A formlet represents one part of a form. It can be combined with other formlets + * to yield new formlets. Formlets are immutable, that is they can be reused in + * as many places as liked. All methods return fresh Formlets instead of muting + * the Formlets they are called upon. + * + * Todo: Rethink the name. The currently proposed "Formlet" seems to have shifted from + * the initial concept of formlets. + * + * Class Formlet + * @package ILIAS\UI\Implementation\Component\Input\Formlet + */ +class Formlet implements IFormlet{ + /** + * Todo: Improve this! What is name? + * @var string + */ + protected $name = "test"; + + /** + * Todo: Improve this! What is type? Ist this needed? Currently only for better + * Todo: readability of the array, dom output. + * readability + * @var string + */ + protected $type = "formlet"; + + /** + * Holds all validations errors in the input + * + * @var ValidationMessageCollector + */ + protected $message_collector = null; + + /** + * Holds all validations to be applied on the POST data + * + * @var Validations + */ + protected $validations; + + /** + * Function value resulting from applying all mappings for this input on each other + * + * @var FV\FunctionValue + */ + protected $mapping = null; + + /** + * Holds wheter the function has been validated, and if, wheter it has been validated + * successfully + * + * @var bool|null + */ + protected $valid = null; + + /** + * Potential children of this input if any. + * + * @var Formlet[] + */ + protected $children = []; + + /** + * Value that this input holds (not mapped). If the input holds children, it also + * holds the values of the children as array. + * + * @return string[]|string|null + */ + protected $value; + + + /** + * Formlet constructor. + * @param array $children + */ + public function __construct($children = []){ + $this->children = $children; + $this->validations = new Validations(); + $this->message_collector = new ValidationMessageCollector(); + $this->mapping = FV\Factory::identity(); + } + + /** + * Todo: do we really need this? + * + * @inheritdoc + */ + public function combine(Input $other){ + return new self([$this, $other]); + } + + /** + * @inheritdoc + */ + public function addMapping(Callable $function){ + $clone = clone $this; + $fv = FV\Factory::functionValue($function); + $clone->mapping = $fv->apply($clone->mapping); + return $clone; + } + + /** + * @inheritdoc + */ + public function map(){ + + if(!$this->valid){ + //Todo: improve this + return "error"; + } + + $mapped_values = []; + + foreach($this->getChildren() as $key => $child){ + $mapped_values[] = $child->map(); + } + + //If children did not return anything or if there are no children, map the value + //the object holds + if(empty($mapped_values)){ + return $this->mapValue($this->getValue()); + } + //If the children did return anything map the values returned by the children + return $this->mapValue($mapped_values); + } + + /** + * @param $value + * @return mixed + * @throws \Exception + */ + protected function mapValue($value){ + $applied_mapping = $this->mapping->apply($value); + return $applied_mapping->get(); + } + + /** + * @inheritdoc + */ + public function addValidation(Validation $validation){ + $clone = clone $this; + $clone->validations = $clone->validations->addValidation($validation); + return $clone; + } + + /** + * @inheritdoc + */ + public function isValid(){ + return $this->valid; + } + + /** + * @inheritdoc + */ + public function isValidated(){ + return $this->valid !== null; + } + + /** + * @inheritdoc + */ + public function getMessageCollector() + { + return $this->message_collector; + } + + /** + * @inheritdoc + */ + public function extractToView(){ + $children = []; + + //Note, that the recursive nature of this currently takes place in the renderer + foreach($this->getChildren() as $key => $child){ + $children[] = $child->setName($this->generateName($this->getName(), + $key,$child->getType())); + } + + return $children; + } + + /** + * @inheritdoc + */ + public function withInputFromView($input){ + $formlet_input = null; + + $clone = $this->withValue($input); + + $clone->valid = $clone->validations->validate($clone->getValue(), $clone->getMessageCollector(),$clone); + + $cloned_children = []; + foreach($clone->getChildren() as $key => $child){ + $cloned_child = clone $child; + + $child_input = $clone->getValue()[$this->generateKey($key, $cloned_child->getType())]; + + $cloned_child = $cloned_child->withInputFromView($child_input); + + if (!$cloned_child->isValid()) { + $clone->valid = false; + $clone->message_collector->join($cloned_child->getMessageCollector()); + } + + $cloned_children[] = $cloned_child; + } + + $clone = $clone->withChildren($cloned_children); + + return $clone; + } + + /** + * @return string + */ + public function getType(){ + return $this->type; + } + + /** + * @return mixed + */ + public function getValue(){ + return $this->value; + } + + /** + * @param $value + * @return Formlet + */ + public function withValue($value) + { + $clone = clone $this; + $clone->value = $value; + + $cloned_children = []; + foreach($clone->getChildren() as $key => $child){ + $cloned_child = clone $child; + if(is_array($value) && array_key_exists($key,$value)){ + $cloned_child = $cloned_child->withValue($value[$key]); + } + + $cloned_children[] = $cloned_child; + } + + $clone = $clone->withChildren($cloned_children); + + return $clone; + } + + /** + * @inheritdoc + */ + public function withChildren($children) + { + $clone = clone $this; + $clone->children = $children; + return $clone; + } + + /** + * @inheritdoc + */ + public function getChildren(){ + return $this->children; + } + + /** + * @inheritdoc + */ + public function hasChildren(){ + return empty($this->getChildren()); + } + + + /** + * Todo: Improve this! What is name? really rethink the naming + * + * Generate the name shown in the DOM for this input element. This is needed by the + * rendered. Using the formName[parentInput][ChildInput] enables PHP the automatically + * read the POST data as nested-array. Note, only the parent can name the child, the + * child can not name itself, since it is unaware of it's parents name. Also note, + * that names can only be given/generated as soon as all input building has been + * completed (since all parents need to be set at this point). + * + * @param $prefix Part of the name that stems from the parents of the element + * @param $counter Number of the element needed to let it be distinguishable from + * it's siblings + * @param $type E.g. 'Text'. Only used for better readability (we only really need + * 'counter') + * @return string + */ + protected function generateName($prefix,$counter,$type){ + return $prefix."[".$this->generateKey($counter,$type)."]"; + } + + /** + * Todo: Improve this! + * See description of generateName. This is needed if only the last part of + * formName[parentInput][ChildInput] is needed. E.g. to get the value of the + * children out of the array of values of the parents + * + * @param $counter + * @param $type + * @return string + */ + protected function generateKey($counter,$type){ + return $type."_".$counter; + } + + + /** + * Todo: Improve this! What is name? really rethink the naming of 'name' ;-) + * @param $name + * @return Formlet + */ + public function setName($name){ + $clone = clone $this; + $clone->name = $name; + return $clone; + } + + /** + * Todo: Improve this! What is name? really rethink the naming of 'name' ;-) + * @return string + */ + public function getName(){ + return $this->name; + } +} diff --git a/src/UI/Implementation/Component/Input/Formlet/FunctionValue/Constant.php b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/Constant.php new file mode 100644 index 000000000000..fb5815df8c19 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/Constant.php @@ -0,0 +1,23 @@ + Extended GPL, see docs/LICENSE */ +namespace ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue; + +/** + * Class Factory + */ +class Factory { + + /** + * Syntactic sugar for using apply multiple times: + * f1->apply(f2)->apply(f3) = compose(f1,f2,f3); + * + * Example: + * $mul = functionValue(function($a,$b){return $a*$b;}); + * $div = functionValue(function($a,$b){return $a/$b;}); + * + * compose($mult,2,$div,12,3) + * ==$mult->apply(2)->apply($div)->apply(12)->apply(3)->get() + * == 2* (12/3) + * == 8 + * + * + * @return \ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue\FunctionValue + */ + public static function compose(){ + $args = func_get_args(); + $temp = array_shift($args); + foreach($args as $arg){ + $temp = $temp->apply($arg); + } + return $temp; + } + + /** + * Returns a new function value by passing a callable + * + * @param $function + * @param null $arity + * @return FunctionValue + */ + public static function functionValue($function, $arity = null) { + return new FunctionValue($function, [], $arity ); + } + + /** + * Return a new invert function + * + * @return Invert + */ + public static function invert() { + return new Invert(); + } + + /** + * Returns a new constant function always returning the passed value + * + * @param $value + * @return Constant + */ + public static function constant($value) { + return new Constant($value); + } + + /** + * Returns a new identity function + * + * @return Identity + */ + public static function identity() { + return new Identity(); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Formlet/FunctionValue/FunctionValue.php b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/FunctionValue.php new file mode 100644 index 000000000000..0da0b1124121 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/FunctionValue.php @@ -0,0 +1,194 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code." + * + * See: https://github.com/lechimp-p/php-formlets + */ +/* Copyright (c) 2016 Timon Amstutz Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue; +use Exception; +use ReflectionFunction; + +/** + * Class FunctionCallable + * @package ILIAS\UI\Implementation\Component\Input\Formlet\Internal\Value + */ +class FunctionValue implements IFunctionValue { + + /** + * Number of arguments the passed function carries that is not matched by + * the set of arguments given for the function + * @var int + */ + protected $arity = 0; + + /** + * Function to be executed as callable. + * @var Callable + */ + protected $function; + + /** + * Set of arguments for the function + * + * @var [] + */ + protected $args; + + + /** + * Create a function value by at least passing it a closure or the name of + * a function. + * One could optionally pass an array of arguments for the first arguments + * of the function to call. This is also used in construction of new function + * values after apply. + * + * ATTENTION: When you pass the name of the function, FunctionValue will not + * know about optional arguments to your function, that is, it will only be + * satisfied when all arguments (event optional ones) are provided. + * + * @throws Exception + * @param $function + * @param array $args + * @param null $arity + */ + public function __construct($function, $args = [], $arity = null) { + if ($arity === null) { + $refl = new ReflectionFunction($function); + $this->arity = $refl->getNumberOfParameters() - count($args); + + } + else { + $this->arity = $arity - count($args); + } + if ($this->arity < 0) { + throw new Exception("FunctionValue::__construct: more args then parameters."); + } + $this->function = $function; + $this->args = $args; + } + + /** + * @inheritdoc + */ + public function isSatisfied() { + return $this->arity === 0; + } + + /** + * @inheritdoc + */ + public function get() { + if(!$this->isSatisfied()){ + throw new Exception("FunctionValue is not satisfied"); + } + + return $this->actualCall(); + } + + /** + * @inheritdoc + */ + public function apply($to) { + //If $to is itself a FunctionValue a new combined Function value is + //created + if($to instanceof FunctionValue){ + return $this->composeWith($to); + } + + + $args = $this->args; + $count_of_old_args = count($this->args); + //$to is added to the args of the new function. This will reduce the + // arity of the new function by one since in the constructor the new + // arity will be calculated by substracting the count of the new args + // with the given arity. + $args[] = $to; + return new self($this->function, $args, $this->arity + + $count_of_old_args); + } + + /** + * Compose this FunctionValue with another and so create a new combined + * FunctionValue + * + * E.g. $mul_div = $multiply->apply($divide); + * Function value $multiply will wrap function value divide. + * $mul_div->apply(6)->apply(3)->apply(5)->get(); + * + * Executing get will execute the function generated in composeWith. This + * function will first apply 6 and 3 to divide. Divide is then satisfied. + * Therefore 5 and the result of divide (2) will be applied to multiply. + * The result will be 10. + * + * @param FunctionValue $other + * @return FunctionValue + */ + protected function composeWith(FunctionValue $other) { + + // The arity of the new FunctionValue will be the two existing + // arities minus one, since the other function will by applied to + // this during the execution of this function. + $arity = $this->getArity()+$other->getArity()-1; + + return new self(function() use ($other) { + //Get the args of this function when called (this is, if get is + //finally executed on the combined function. + $args = func_get_args(); + $used_args = []; + //Apply the arguments as long as necessary on the other (inner + // function) + foreach($args as $arg){ + if(!$other->isSatisfied()){ + $other = $other->apply($arg); + $used_args[] = $arg; + } + } + + //Get the args that have not been used for the other function... + $args = array_diff_assoc($args,$used_args); + + //... and apply them on this + $temp_this = $this; + foreach($args as $arg){ + $temp_this = $temp_this->apply($arg); + } + + //Actual result that will be returned on executing get. The other + //function is wrapped and executed inside this. + return $temp_this->apply($other->get())->get(); + },$other->getArgs(),$arity); + } + + /** + * Executes the actual call + * + * @return mixed + */ + protected function actualCall() { + return call_user_func_array($this->function, $this->args); + } + + /** + * @return int + */ + public function getArity() + { + return $this->arity; + } + + /** + * @return mixed + */ + public function getArgs() + { + return $this->args; + } +} diff --git a/src/UI/Implementation/Component/Input/Formlet/FunctionValue/IFunctionValue.php b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/IFunctionValue.php new file mode 100644 index 000000000000..54bf1a27e87e --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/IFunctionValue.php @@ -0,0 +1,52 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code." + * + * See: https://github.com/lechimp-p/php-formlets + */ +/* Copyright (c) 2016 Timon Amstutz Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue; + +/** + * Function Values work around the problem, that functions could not be used as + * ordinary values easily in PHP. + * + * A value either wraps a plain value in an underlying PHP-Representation or + * is a possibly curried function that could be applied to other values. + */ +interface IFunctionValue { + + /** + * Returns whether the has been applied enough times to have a result. + * + * @return bool + */ + public function isSatisfied(); + + + /** + * Get the value from the result of the function if it is + * satisfied. Throw otherwise. + * + * If the function is satisfied get the result. + * + * @return mixed + */ + public function get(); + + /** + * Apply the function to a value, creating a new function value with one + * less arity. + * + * @param $to + * @return FunctionValue + */ + public function apply($to); +} diff --git a/src/UI/Implementation/Component/Input/Formlet/FunctionValue/Identity.php b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/Identity.php new file mode 100644 index 000000000000..1db15511a992 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/Identity.php @@ -0,0 +1,24 @@ +apply(" "); + +// We could apply the function to the string to create the final result: +$res = $explodeBySpace->apply("foo bar"); + +// Since the value is still wrapped, we need to unwrap it. +$unwrapped = $res->get(); + +echo "Array containing \"foo\" and \"bar\":\n"; +print_r($unwrapped); +?> +``` + +See: [An implementation of the Formlets-abstraction in PHP](https://github.com/jbt/markdown-editor.com/lechimp-p/php-formlets). + +Copyright (c) 2014 Richard Klees \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Formlet/FunctionValue/example.php b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/example.php new file mode 100644 index 000000000000..28d198ca7fc7 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/FunctionValue/example.php @@ -0,0 +1,26 @@ +apply(" "); + +// We could apply the function to the string to create the final result: +$res = $explodeBySpace->apply("foo bar"); + +// Since the value is still wrapped, we need to unwrap it. +$unwrapped = $res->get(); + +echo "Array containing \"foo\" and \"bar\":\n"; +print_r($unwrapped); + diff --git a/src/UI/Implementation/Component/Input/Formlet/IFormlet.php b/src/UI/Implementation/Component/Input/Formlet/IFormlet.php new file mode 100644 index 000000000000..d1e3c8a25272 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/IFormlet.php @@ -0,0 +1,46 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code." + * + * See: https://github.com/lechimp-p/php-formlets + */ +/* Copyright (c) 2016 Timon Amstutz Extended GPL, see docs/LICENSE */ +namespace ILIAS\UI\Implementation\Component\Input\Formlet; + +use \ILIAS\UI\Component\Input\Input; +/** + * A formlet represents one part of a form. It can be combined with other formlets + * to yield new formlets. Formlets are immutable, that is they can be reused in + * as many places as liked. All methods return fresh Formlets instead of muting + * the Formlets they are called upon. + * + * Todo: Rethink the name. The currently proposed "Formlet" seems to have shifted from + * the initial concept of formlets. + */ +interface IFormlet extends Input{ + /** + * Internally used to get the content to be rendered by the renderer + * + * @return array + */ + public function extractToView(); + + /** + * Todo: Improve this! What is name? + * @param string $name + * @return Input + */ + public function setName($name); + + /** + * Todo: Improve this! What is name? + * @return string + */ + public function getName(); +} diff --git a/src/UI/Implementation/Component/Input/Formlet/Renderer.php b/src/UI/Implementation/Component/Input/Formlet/Renderer.php new file mode 100755 index 000000000000..47bfa08cfa94 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/Renderer.php @@ -0,0 +1,36 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Formlet; + +use ILIAS\UI\Implementation\Render\AbstractComponentRenderer; +use ILIAS\UI\Renderer as RendererInterface; +use ILIAS\UI\Component; + +/** + * Class Renderer + * @package ILIAS\UI\Implementation\Component\Image + */ +class Renderer extends AbstractComponentRenderer { + /** + * @inheritdocs + */ + public function render(Component\Component $component, RendererInterface $default_renderer) { + /** + * @var \ILIAS\UI\Implementation\Component\Input\Formlet\Formlet $component + */ + $content = ""; + foreach($component->extractToView() as $item){ + $content .= $default_renderer->render($item); + } + return $content; + } + + /** + * @inheritdocs + */ + protected function getComponentInterfaceName() { + return [Component\Input\Item\Formlet\Formlet::class]; + } +} diff --git a/src/UI/Implementation/Component/Input/Formlet/example.php b/src/UI/Implementation/Component/Input/Formlet/example.php new file mode 100644 index 000000000000..735080cb4f9e --- /dev/null +++ b/src/UI/Implementation/Component/Input/Formlet/example.php @@ -0,0 +1,81 @@ +formlet("test"); +$formlet = $formlet->addViewToModelMapping( + function($input){ + var_dump($input); + return 2*$input; + } +); +$formlet = $formlet->addValidation((new V\Validation(function($input){ + return $input == 3;//is_numeric($input); +},"Error1"))); + + +$formlet = $formlet->combine($formlet->createClone("test2")); + + +$formlet = $formlet->withInputFromView(["test2"=>3,"test"=>3]); +var_dump($formlet->getMessageCollector()->getMessages()); + +var_dump($formlet->extractToView()); +var_dump($formlet->extractToModel()); +var_dump($formlet->extractToModel()); +exit; +/** +$formlet = $formlet->withInputFromView("asdf asdf"); +var_dump($formlet->extractToModel()); +var_dump($formlet->isValid()); + + + + + +$value = F::getFactory()->value()->plain("1000000"); + +$view_to_model = new V\FunctionCallable(function($input){ + return intval($input); +}); + +$model_to_view = new V\FunctionCallable(function($input){ + return "Value: ".$input; + +}); +$model_to_view = $model_to_view->composeWith(new V\FunctionCallable(function +($input){ + return number_format ($input); +})); + +$map = new R\Map($model_to_view,$view_to_model); + + +var_dump($map->viewToModel($value)); +var_dump($map->modelToView($value)); + + + +$view_validation = new V\FunctionCallable(function($input){ + return is_numeric($input); +}); + +$model_validation = new V\FunctionCallable(function($input){ + return is_int($input); + +}); + +$validation = new R\Validation($view_validation,$model_validation); + +$value1 = F::getFactory()->value()->plain("1000000"); + +$value2= F::getFactory()->value()->plain(1000000); + +var_dump($validation->validateView($value1)); +var_dump($validation->validateModel($value2));**/ \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Item/Factory.php b/src/UI/Implementation/Component/Input/Item/Factory.php new file mode 100755 index 000000000000..892665e5f48d --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Factory.php @@ -0,0 +1,32 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Item; + +use ILIAS\UI\Component\Input\Item as I; +use ILIAS\UI\Implementation\Component\ComponentHelper; + +/** + * Class Factory + * + * @package ILIAS\UI\Implementation\Component\Filter + */ +class Factory implements I\Factory { + + use ComponentHelper; + + /** + * @inheritdoc + */ + public function field() { + return new Field\Factory(); + } + + /** + * @inheritdoc + */ + public function selector() { + return new Selector\Factory(); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Item/Field/Factory.php b/src/UI/Implementation/Component/Input/Item/Field/Factory.php new file mode 100755 index 000000000000..e989e044eadf --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Field/Factory.php @@ -0,0 +1,39 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Item\Field; + +use ILIAS\UI\Component\Input\Item\Field as F; +use ILIAS\UI\Implementation\Component\ComponentHelper; + +/** + * Class Factory + * + * @package ILIAS\UI\Implementation\Component\Filter + */ +class Factory implements F\Factory { + + use ComponentHelper; + + /** + * @inheritdoc + */ + public function text($label) { + return new Text($label,[]); + } + + /** + * @inheritdoc + */ + public function number($label) { + return new Number($label); + } + + /** + * @inheritdoc + */ + public function nameAge($id) { + return new NameAge($id, ""); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Item/Field/NameAge.php b/src/UI/Implementation/Component/Input/Item/Field/NameAge.php new file mode 100644 index 000000000000..bc93d9b8635f --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Field/NameAge.php @@ -0,0 +1,21 @@ + Extended GPL, see docs/LICENSE */ + + +namespace ILIAS\UI\Implementation\Component\Input\Item\Field; + +use ILIAS\UI\Component\Input\Item\Field as F; +use ILIAS\UI\Implementation\Component\Input\Item as I; + +class Number extends I\Item implements F\Text { + /** + * @var string + */ + protected $type = "number"; + + /** + * @inheritdoc + */ + public function __construct($label) { + parent::__construct($label); + } +} diff --git a/src/UI/Implementation/Component/Input/Item/Field/Renderer.php b/src/UI/Implementation/Component/Input/Item/Field/Renderer.php new file mode 100755 index 000000000000..45a841e62632 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Field/Renderer.php @@ -0,0 +1,72 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Item\Field; + +use ILIAS\UI\Implementation\Render\AbstractComponentRenderer; +use ILIAS\UI\Renderer as RendererInterface; +use ILIAS\UI\Component; + +/** + * Class Renderer + * Todo this is mostly experimenting + * @package ILIAS\UI\Implementation\Component\Image + */ +class Renderer extends AbstractComponentRenderer { + /** + * @inheritdocs + */ + public function render(Component\Component $component, RendererInterface $default_renderer) { + /** + * @var \ILIAS\UI\Implementation\Component\Input\Item\Item $component + */ + $this->checkComponent($component); + $tpl = $this->getTemplate("Filter/tpl.text.html", true, true); + + if($component instanceof NameAge) + { + $inputs =""; + foreach($component->extractToView() as $option){ + $inputs .= $default_renderer->render($option); + } + return $inputs; + }else{ + //$tpl->setVariable("ID","TODO"); + $tpl->setVariable("FOR",$component->getName()); + $tpl->setVariable("ID",$component->getName()); + $tpl->setVariable("VALUE",$component->getValue()); + + + if($component->isValidated()){ + if($component->isValid()){ + $tpl->touchBlock("success"); + }else{ + $tpl->touchBlock("error"); + $tpl->setVariable("VALIDATION_ERROR", + $component->getMessageCollector()->getMessages()[0]->getMessage()); + } + + } + + + $tpl->setVariable("LABEL",$component->getLabel()); + + if($component->isRequired()){ + $tpl->setVariable("REQUIRED","required"); + }else{ + $tpl->setVariable("REQUIRED",""); + } + + return $tpl->get(); + } + + } + + /** + * @inheritdocs + */ + protected function getComponentInterfaceName() { + return [Component\Input\Item\Field\Text::class]; + } +} diff --git a/src/UI/Implementation/Component/Input/Item/Field/Text.php b/src/UI/Implementation/Component/Input/Item/Field/Text.php new file mode 100755 index 000000000000..6a03cf035f3e --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Field/Text.php @@ -0,0 +1,28 @@ + Extended GPL, see docs/LICENSE */ + + +namespace ILIAS\UI\Implementation\Component\Input\Item\Field; + +use ILIAS\UI\Component\Input\Item\Field as F; +use ILIAS\UI\Implementation\Component\Input\Item as I; + +/** + * Class Text + * @package ILIAS\UI\Implementation\Component\Input\Item\Field + */ +class Text extends I\Item implements F\Text { + + /** + * @var string + */ + protected $type = "text"; + + /** + * @inheritdoc + */ + public function __construct($label) { + parent::__construct($label); + } +} diff --git a/src/UI/Implementation/Component/Input/Item/Item.php b/src/UI/Implementation/Component/Input/Item/Item.php new file mode 100755 index 000000000000..ca4480b5cb9c --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Item.php @@ -0,0 +1,58 @@ +checkStringArg("label",$label); + $this->label = $label; + parent::__construct($children); + } + + /** + * @inheritdocs + */ + public function getLabel(){ + return $this->label; + } + + /** + * @inheritdocs + */ + public function required(){ + $clone = clone $this; + $clone->required = true; + $clone = $clone->addValidation(new F\NotEmpty()); + return $clone; + } + + /** + * @inheritdocs + */ + public function isRequired(){ + return $this->required; + } +} diff --git a/src/UI/Implementation/Component/Input/Item/Selector/Factory.php b/src/UI/Implementation/Component/Input/Item/Selector/Factory.php new file mode 100755 index 000000000000..e4daaefc29bf --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Selector/Factory.php @@ -0,0 +1,38 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Item\Selector; + +use ILIAS\UI\Component\Input\Item\Selector as S; + +/** + * Class Factory + * + * @package ILIAS\UI\Implementation\Component\Filter + */ +class Factory implements S\Factory { + + public function repository(){ + + } + + public function radioGroup($id,$label,$radio_options){ + return new RadioGroup($id,$label,$radio_options); + + } + + /** + * --- + * description: + * purpose: > + * Todo + * + * ---- + * @param RadioOption[] $radio_options Radio options to be offered by the radio Group + * @return \ILIAS\UI\Component\Input\Item\Selector\RadioGroup + */ + public function radioOption($id,$label){ + return new RadioOption($id,$label); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Item/Selector/RadioGroup.php b/src/UI/Implementation/Component/Input/Item/Selector/RadioGroup.php new file mode 100755 index 000000000000..90a3bf4c7c9e --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Selector/RadioGroup.php @@ -0,0 +1,28 @@ +inGroup($this->getId()); + $children_copy[] = $child; + } + parent::__construct($id,$label,$children_copy); + } +} diff --git a/src/UI/Implementation/Component/Input/Item/Selector/RadioOption.php b/src/UI/Implementation/Component/Input/Item/Selector/RadioOption.php new file mode 100755 index 000000000000..a9e0b83254a1 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Selector/RadioOption.php @@ -0,0 +1,25 @@ +group_id = $id; + } + + public function getGroup(){ + return $this->group_id; + } + +} diff --git a/src/UI/Implementation/Component/Input/Item/Selector/Renderer.php b/src/UI/Implementation/Component/Input/Item/Selector/Renderer.php new file mode 100755 index 000000000000..85fa8adcc7cd --- /dev/null +++ b/src/UI/Implementation/Component/Input/Item/Selector/Renderer.php @@ -0,0 +1,82 @@ +checkComponent($component); + + if($component instanceof Component\Input\Item\Selector\RadioGroup) + { + $tpl = $this->getTemplate("Item/Selector/tpl.radio_group.html", true, true); + + $options = ""; + + foreach($component->extractToView() as $option){ + $options .= $default_renderer->render($option); + } + $tpl->setVariable("OPTIONS",$options); + }else{ + $tpl = $this->getTemplate("Item/Selector/tpl.radio_option.html", true, true); + $tpl->setVariable("GROUP",$component->getGroup()); + + + } + + + + //$tpl->setVariable("ID","TODO"); + $tpl->setVariable("FOR",$component->getId()); + $tpl->setVariable("ID",$component->getId()); + $tpl->setVariable("VALUE",$component->extractToView()); + + + if($component->isValidated()){ + if($component->isValid()){ + $tpl->touchBlock("success"); + }else{ + $tpl->touchBlock("error"); + $tpl->setVariable("VALIDATION_ERROR", + $component->getMessageCollector()->getMessages()[0]->getMessage()); + } + + } + + + $tpl->setVariable("LABEL",$component->getLabel()); + + if($component->isRequired()){ + $tpl->setVariable("REQUIRED","required"); + }else{ + $tpl->setVariable("REQUIRED",""); + } + + return $tpl->get(); + } + + /** + * @inheritdocs + */ + protected function getComponentInterfaceName() { + return [ + Component\Input\Item\Selector\RadioOption::class, + Component\Input\Item\Selector\RadioGroup::class, + ]; + } +} diff --git a/src/UI/Implementation/Component/Input/Validation/Custom.php b/src/UI/Implementation/Component/Input/Validation/Custom.php new file mode 100755 index 000000000000..2ff2c14372ae --- /dev/null +++ b/src/UI/Implementation/Component/Input/Validation/Custom.php @@ -0,0 +1,9 @@ + Extended GPL, see docs/LICENSE */ + +namespace ILIAS\UI\Implementation\Component\Input\Validation; + +use ILIAS\UI\Component\Input\Validation as I; +use ILIAS\UI\Implementation\Component\ComponentHelper; + +/** + * Class Factory + * + * @package ILIAS\UI\Implementation\Component\Filter + */ +class Factory implements \ILIAS\UI\Component\Input\Validation\Factory { + + use ComponentHelper; + + /** + * @inheritdoc + */ + public function custom(callable $validation, $message) { + return new Custom($validation, $message); + } + + /** + * @inheritdoc + */ + public function Regex($regex) { + return new Regex($regex); + + } + + /** + * @inheritdoc + */ + public function notEmpty() { + return new notEmpty(); + } + + /** + * @inheritdoc + */ + public function equals($to_be_equaled) { + return new Equals($to_be_equaled); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Validation/NotEmpty.php b/src/UI/Implementation/Component/Input/Validation/NotEmpty.php new file mode 100755 index 000000000000..a6022fc85e02 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Validation/NotEmpty.php @@ -0,0 +1,19 @@ +method = $f->functionValue($function); + $this->message_text = $message_text; + } + + /** + * @inheritdoc + */ + public function getMessageText(){ + return $this->message_text; + } + + /** + * @inheritdoc + */ + public function getValidationMethod(){ + return $this->method; + } + + /** + * @inheritdoc + */ + public function invert(){ + $clone = clone $this; + $f = new F\Factory(); + $invert = $f->invert(); + $clone->method = $invert->apply($this->method); + return $clone; + } + + /** + * @inheritdoc + */ + public function validate($value, V\ValidationMessageCollector $collector, Item $item){ + if($this->method->apply($value)->get()){ + return true; + } + + $collector->addMessage(new ValidationMessage($this->getMessageText(), $item)); + return false; + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Validation/ValidationMessage.php b/src/UI/Implementation/Component/Input/Validation/ValidationMessage.php new file mode 100644 index 000000000000..2e93da754422 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Validation/ValidationMessage.php @@ -0,0 +1,40 @@ +message = $message; + $this->item = $item; + } + + /** + * @inheritdoc + */ + public function getMessage(){ + return $this->message; + } + + /** + * @inheritdoc + */ + public function getItem(){ + return $this->item; + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Validation/ValidationMessageCollector.php b/src/UI/Implementation/Component/Input/Validation/ValidationMessageCollector.php new file mode 100644 index 000000000000..29f5c41d7284 --- /dev/null +++ b/src/UI/Implementation/Component/Input/Validation/ValidationMessageCollector.php @@ -0,0 +1,69 @@ +messages[] = $message; + } + + /** + * @inheritdoc + */ + public function getMessages(){ + return $this->messages; + } + + /** + * @inheritdoc + */ + public function join(V\ValidationMessageCollector $collector){ + $this->messages = array_merge($this->messages,$collector->getMessages()); + } + + /** + * Iterator implementations + * + * @return bool + */ + public function valid() { + return current($this->messages) !== false; + } + + /** + * @return ValidationMessage + */ + public function key() { + return key($this->messages); + } + + /** + * @return ValidationMessage + */ + public function current() { + return current($this->messages); + } + + public function next() { + next($this->messages); + } + + public function rewind() { + reset($this->messages); + } +} \ No newline at end of file diff --git a/src/UI/Implementation/Component/Input/Validation/Validations.php b/src/UI/Implementation/Component/Input/Validation/Validations.php new file mode 100644 index 000000000000..8078d14f887f --- /dev/null +++ b/src/UI/Implementation/Component/Input/Validation/Validations.php @@ -0,0 +1,67 @@ +validations = $validations; + } + + /** + * @param Validation $validation + * @return Validations + */ + public function addValidation(Validation $validation){ + $clone = clone $this; + $clone->validations[] = $validation; + return $clone; + } + + /** + * Performs the actual validation with all the validations given in sequence. + * + * @param $value + * @param $collector + * @param $item + * @return bool + */ + public function validate($value, $collector,$item){ + $valid = true; + foreach($this->validations as $validation){ + if(!$validation->validate($value,$collector,$item)){ + $valid = false; + } + } + return $valid; + } + + /** + * @return F\FunctionValue + */ + public function getValidations() + { + return $this->validations; + } + +} diff --git a/src/UI/Implementation/Factory.php b/src/UI/Implementation/Factory.php index f9eae9e94a62..6a619f8d0091 100644 --- a/src/UI/Implementation/Factory.php +++ b/src/UI/Implementation/Factory.php @@ -89,4 +89,11 @@ public function modal() { return new Component\Modal\Factory(new SignalGenerator()); } + /** + * @inheritdoc + */ + public function input() + { + return new Component\Input\Factory(); + } } diff --git a/src/UI/examples/Input/Container/Form/Standard/base.php b/src/UI/examples/Input/Container/Form/Standard/base.php new file mode 100755 index 000000000000..76c1adba83c9 --- /dev/null +++ b/src/UI/examples/Input/Container/Form/Standard/base.php @@ -0,0 +1,92 @@ +ui()->factory(); + $renderer = $DIC->ui()->renderer(); + + //Collecting all inputs that will be stored in the form + $items = []; + + //Minimal textfield + $items[] = $f->input()->item()->field()->text("Minimal"); + + //Required textfield with default value and standard validation + $items[] = $f->input()->item()->field()->text("Required") + ->required(); + + //Textfield with default value and standard validation + $items[] = $f->input()->item()->field()->text("Default Value and Validation") + ->required() + ->withValue(1) + ->addValidation($f->input()->validation()->equals(3)); + + //Required textfield with custom validation + $items[] = $f->input()->item()->field()->text("Bigger than 3") + ->required() + ->withValue(1)->addValidation( + $f->input()->validation()->custom( + function ($input){ + return $input > 3; + },"Not bigger than 3")); + + //Textfield with chained mapping. Will convert input to int, double it and wrap + //some text around it. + $items[] = $f->input()->item()->field()->text("Combined Mapping") + ->withValue(3) + ->addMapping( + function($input){ + return intval($input)*2; + }) + ->addMapping( + function($input){ + return "Output to Model is Input times 2: ".$input; + }); + + //Combined Input with chained mapping. First map into an array, then into some object. + $structured_output = new stdClass(); + + $item1 = $f->input()->item()->field()->text("Part of Combined Input"); + + $items[] = $item1->combine($item1) + ->addMapping( + function($input){ + return ["item 1"=>$input[0],"item 2"=>$input[1]]; + }) + ->addMapping( + function($input) use ($structured_output) { + $structured_output->item1 = $input["item 1"]; + $structured_output->item2 = $input["item 2"]; + }); + + //Create form with inputs + $form = $f->input()->container()->form()->standard("#","Test Form",$items); + + + //Show some output if form has been sent; + $output = ""; + if($_POST){ + //Store post input into form. The input will be validated automatically. + //Todo, the the key test is due to yet improper handling of keys. + $form = $form->withInputFromView($_POST['test']); + if($form->isValid()){ + $output .= "Array:
"; + $output .=print_r($form->map(),true)."
"; + $output .= "Object:
"; + $output .=print_r($structured_output,true)."
"; + }else{ + $output .= "Invalid Input
"; + + foreach($form->getMessageCollector() as $message){ + //Todo: there is no clear concept yet for item name. + $output .= $message->getItem()->getName().":
"; + $output .= $message->getMessage()."
"; + } + } + } + + return $renderer->render($form).$output; +} diff --git a/src/UI/examples/Input/Container/Form/Sub/base.php b/src/UI/examples/Input/Container/Form/Sub/base.php new file mode 100644 index 000000000000..0e071083cfa0 --- /dev/null +++ b/src/UI/examples/Input/Container/Form/Sub/base.php @@ -0,0 +1,38 @@ +ui()->factory(); + $renderer = $DIC->ui()->renderer(); +/** + $items = []; + $options = []; + $options[] = $f->input()->item()->selector()->radioOption("radio_option1", "Radio 1"); + $options[] = $f->input()->item()->selector()->radioOption("radio_option2","Radio 2 Sub 2")->combineWithSubform( + $f->input()->container()->form()->sub( + [$f->input()->item()->field()->text("id15","Section Sub Sub Textfield 1")] + )); + + $items[] = $f->input()->item()->selector()->radioGroup("radio_group","Radio Group", $options); + + + $form = $f->input()->container()->form()->standard("#","Test Form",$items); + + $output = ""; + if($_POST){ + $form->withInputFromView($_POST); + if($form->isValid()){ + foreach($form->extractToModel() as $out){ + $output .= $out; + } + + }else{ + $output .= "Invalid Input"; + } + }**/ + + return "";//$renderer->render($form).$output; +} diff --git a/src/UI/examples/Input/Item/Field/Text/base.php b/src/UI/examples/Input/Item/Field/Text/base.php new file mode 100755 index 000000000000..1eab71c0eb43 --- /dev/null +++ b/src/UI/examples/Input/Item/Field/Text/base.php @@ -0,0 +1,30 @@ +ui()->factory(); + $renderer = $DIC->ui()->renderer(); + + //Genarating and rendering the text input + $text_input = $f->input()->item()->field()->text("id","Textfield"); + $text_input->withInputFromModel(["id"=>"test"]); + $html = $renderer->render($text_input); + + $text_input = $f->input()->item()->field()->text("id","Textfield"); + $text_input = $text_input->addValidation( + $f->input()->validation()->equals(3)); + + + $text_input->withInputFromView(["id"=>"testView"]); + + $html .= $renderer->render($text_input); + + $text_input->withInputFromView(["id"=>"3"]); + + $html .= $renderer->render($text_input); + + return $html; +} diff --git a/src/UI/examples/Modal/Interruptive/async.js b/src/UI/examples/Modal/Interruptive/async.js new file mode 100644 index 000000000000..257f1b30924f --- /dev/null +++ b/src/UI/examples/Modal/Interruptive/async.js @@ -0,0 +1,16 @@ +il.Async.get = function(url, callback){ + $.ajax({ + type: "GET", + url: url, + }).done(function (return_data) { + if (return_data.success) { + $("body").append(return_data.content); + if (callback instanceof Function) { + //This would be probably the show action of the Modal + callback(); + } + } + else { + //Some error processing + }}); +} diff --git a/src/UI/examples/Modal/Interruptive/show_modal_on_button_click.php b/src/UI/examples/Modal/Interruptive/show_modal_on_button_click.php index fc034b3266ff..61af1d61c8ce 100644 --- a/src/UI/examples/Modal/Interruptive/show_modal_on_button_click.php +++ b/src/UI/examples/Modal/Interruptive/show_modal_on_button_click.php @@ -1,30 +1,28 @@ ui()->factory(); - $renderer = $DIC->ui()->renderer(); - $message = 'Are you sure you want to delete the following items?'; - $form_action = $DIC->ctrl()->getFormActionByClass('ilsystemstyledocumentationgui'); - $icon = $factory->image()->standard('./templates/default/images/icon_crs.svg', ''); - $modal = $factory->modal()->interruptive('My Title', $message, $form_action) - ->withAffectedItems(array( - $factory->modal()->interruptiveItem(10, 'Course 1', $icon, 'Some description text'), - $factory->modal()->interruptiveItem(20, 'Course 2', $icon, 'Another description text'), - $factory->modal()->interruptiveItem(30, 'Course 3', $icon, 'Last but not least, a description'), - )); - $button = $factory->button()->standard('Show Modal', '') - ->withOnClick($modal->getShowSignal()); + global $DIC; + $factory = $DIC->ui()->factory(); + $renderer = $DIC->ui()->renderer(); + $content = $factory->legacy('This modal was opened on button click'); + $modal = $factory->modal()->interruptive('Modal Title', $content) + ->withActionButton($factory->button()->primary('Delete', '')); + $button = $factory->button()->standard('Open Modal', ''); + + return $renderer->render($button->triggerAction($modal->show())); +} + +$button->triggerAction($modal->showAsync($returnURL)); + + +function showAsync($returnURL) +{ + $action = new TriggerAction($this); + $action->setJavascriptBinding(function ($id) { + return "il.Async.get($returnURL);"; + }); + + return $action; +} - // Display POST data of affected items in a panel - $panel = ''; - if (isset($_POST['interruptive_items'])) { - $panel = $factory->panel()->standard( - 'Affected Items', - $factory->legacy(print_r($_POST['interruptive_items'], true)) - ); - $panel = $renderer->render($panel); - } - return $renderer->render([$button, $modal]) . $panel; -} \ No newline at end of file diff --git a/src/UI/templates/default/Input/Filter/tpl.text.html b/src/UI/templates/default/Input/Filter/tpl.text.html new file mode 100755 index 000000000000..0f164b4fda1a --- /dev/null +++ b/src/UI/templates/default/Input/Filter/tpl.text.html @@ -0,0 +1,11 @@ +
+ +
+ +
{VALIDATION_ERROR}
+
{DESCRIPTION}
+
+
\ No newline at end of file diff --git a/src/UI/templates/default/Input/Form/tpl.section.html b/src/UI/templates/default/Input/Form/tpl.section.html new file mode 100755 index 000000000000..5dd2c8e38dae --- /dev/null +++ b/src/UI/templates/default/Input/Form/tpl.section.html @@ -0,0 +1,4 @@ +

{SECTION_TITLE}

+
+
+{CONTENT} \ No newline at end of file diff --git a/src/UI/templates/default/Input/Form/tpl.standard.html b/src/UI/templates/default/Input/Form/tpl.standard.html new file mode 100755 index 000000000000..bb6f688f4b07 --- /dev/null +++ b/src/UI/templates/default/Input/Form/tpl.standard.html @@ -0,0 +1,17 @@ +
+
+

{FORM_TITLE}

+
+ +
+
+
+ {CONTENT_DEFAULT_SECTION} + {ADDITIONAL_SECTIONS} +
+
+ +
+
+
+
\ No newline at end of file diff --git a/src/UI/templates/default/Input/Form/tpl.sub.html b/src/UI/templates/default/Input/Form/tpl.sub.html new file mode 100755 index 000000000000..9e03b00aba9d --- /dev/null +++ b/src/UI/templates/default/Input/Form/tpl.sub.html @@ -0,0 +1,7 @@ +
+ {CONTENT} +
+ + + + \ No newline at end of file diff --git a/src/UI/templates/default/Input/Item/Selector/tpl.radio_group.html b/src/UI/templates/default/Input/Item/Selector/tpl.radio_group.html new file mode 100755 index 000000000000..4c472b046a59 --- /dev/null +++ b/src/UI/templates/default/Input/Item/Selector/tpl.radio_group.html @@ -0,0 +1,10 @@ +
+ +
+ {OPTIONS} +
{VALIDATION_ERROR}
+
{DESCRIPTION}
+
+
\ No newline at end of file diff --git a/src/UI/templates/default/Input/Item/Selector/tpl.radio_option.html b/src/UI/templates/default/Input/Item/Selector/tpl.radio_option.html new file mode 100755 index 000000000000..806172023fec --- /dev/null +++ b/src/UI/templates/default/Input/Item/Selector/tpl.radio_option.html @@ -0,0 +1,8 @@ +
+ +
{VALIDATION_ERROR}
+
{DESCRIPTION}
+
\ No newline at end of file diff --git a/tests/UI/Base.php b/tests/UI/Base.php index 12532bba96b7..fce43d500db5 100644 --- a/tests/UI/Base.php +++ b/tests/UI/Base.php @@ -30,6 +30,7 @@ public function image() {} public function legacy($content) {} public function panel() {} public function modal() {} + public function input() {} } class LoggingRegistry implements ResourceRegistry { diff --git a/tests/UI/Component/Input/FunctionValueTest.php b/tests/UI/Component/Input/FunctionValueTest.php new file mode 100644 index 000000000000..ab9dd8c3fe0c --- /dev/null +++ b/tests/UI/Component/Input/FunctionValueTest.php @@ -0,0 +1,218 @@ + + * + * This software is licensed under The MIT License. You should have received + * a copy of the along with the code." + * + * See: https://github.com/lechimp-p/php-formlets + */ +namespace ILIAS\UI\Implementation\Component\Input\Formlet\Factory\Test\Value; + +require_once("libs/composer/vendor/autoload.php"); + +use \ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue\Factory as F; +use PHPUnit_Framework_TestCase; + +/** + * Class PlainValueTest + * @package ILIAS\UI\Implementation\Component\Input\Formlet\Factory\Test\Value + */ +class FunctionValueTest extends PHPUnit_Framework_TestCase { + /** + * @return array + */ + public function getMultiplyFunction() { + return F::functionValue(function($a,$b){ + return $a*$b; + }); + } + + /** + * @return \ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue\FunctionValue + */ + public function getDivideFunction(){ + return F::functionValue(function($a,$b){ + return $a/$b; + }); + } + + /** + * @return \ILIAS\UI\Implementation\Component\Input\Formlet\FunctionValue\FunctionValue + */ + public function getEqualsFunction(){ + return F::functionValue(function($a,$b){ + return $a==$b; + }); + } + + public function testGet(){ + $const = F::constant(5); + $this->assertEquals(5,$const->get()); + } + + public function testSingleApply(){ + $id = F::identity(); + $this->assertEquals(3,$id->apply(3)->get()); + } + + public function testMultiApply1(){ + $mult = $this->getMultiplyFunction(); + $this->assertEquals(12,$mult->apply(3)->apply(4)->get()); + } + + public function testMultiApply2(){ + $equals = $this->getEqualsFunction(); + $this->assertTrue($equals->apply(3)->apply(3)->get()); + $this->assertFalse($equals->apply(3)->apply(4)->get()); + + } + + public function testComposeApply1(){ + $invert = F::invert(); + $equals = $this->getEqualsFunction(); + + $this->assertTrue($invert + ->apply($equals) + ->apply(4) + ->apply(3) + ->get() + ); + } + + public function testComposeApply2(){ + $mult = $this->getMultiplyFunction(); + $fun = $mult + ->apply(4) + ->apply($mult) + ->apply(4) + ->apply(3); + $this->assertEquals(48, $fun->get()); + + } + + public function testComposeApply3(){ + $mult = $this->getMultiplyFunction(); + + $this->assertEquals(48,$mult + ->apply($mult) + ->apply(4) + ->apply(4) + ->apply(3) + ->get() + ); + } + + public function testComposeApply4(){ + $mult = $this->getMultiplyFunction(); + $divide = $this->getDivideFunction(); + + $this->assertEquals(16,$mult + ->apply(4) + ->apply($divide) + ->apply(12) + ->apply(3) + ->get() + ); + } + + public function testComposeApply5() + { + $mult = $this->getMultiplyFunction(); + $divide = $this->getDivideFunction(); + + $this->assertEquals(1, $mult + ->apply($divide) + ->apply(4) + ->apply(12) + ->apply(3) + ->get() + ); + } + + public function testComposeEqualsApply(){ + $mult = $this->getMultiplyFunction(); + $div = $this->getDivideFunction(); + + $composed = F::compose($mult,2,$div,12,3); + + + $this->assertEquals(8,$composed->get()); + $this->assertEquals( + $mult->apply(2)->apply($div)->apply(12)->apply(3)->get(), + $composed->get() + ); + } + + public function testCompose1(){ + $invert = F::invert(); + $equals = $this->getEqualsFunction(); + + $composed = F::compose($invert,$equals,4,3); + + $this->assertTrue($composed->get()); + } + + public function testCompose2(){ + $mult = $this->getMultiplyFunction(); + $composed = F::compose($mult,4,$mult,4,3); + + $this->assertEquals(48, $composed->get()); + } + + public function testCompose3(){ + $mult = $this->getMultiplyFunction(); + $composed = F::compose($mult,$mult,4,4,3); + $this->assertEquals(48,$composed->get()); + } + + public function testCompose4(){ + $mult = $this->getMultiplyFunction(); + $divide = $this->getDivideFunction(); + $composed = F::compose($mult,4,$divide,12,3); + + $this->assertEquals(16,$composed->get()); + } + + public function testCompose5() + { + $mult = $this->getMultiplyFunction(); + $divide = $this->getDivideFunction(); + $composed = F::compose($mult,$divide,4,12,3); + $this->assertEquals(1, $composed->get()); + } + + public function testComposeVariant1() + { + $comp = $this->getEqualsFunction(); + $divide = $this->getDivideFunction(); + + $devision_equals_3 = F::compose($comp,3,$divide); + + $this->assertTrue( $devision_equals_3->apply(9)->apply(3)->get()); + $this->assertFalse( $devision_equals_3->apply(12)->apply(3)->get()); + + } + + public function testExplodeExample(){ + $explode = F::functionValue("explode", 2); + + $explodeBySpace = $explode->apply(" "); + $res = $explodeBySpace->apply("foo bar"); + + $this->assertEquals(['foo','bar'],$res->get()); + } + + public function testDescriptionExample(){ + $mult = $this->getMultiplyFunction(); + $divide = $this->getDivideFunction(); + + $mul_div = $mult->apply($divide); + $res = $mul_div->apply(6)->apply(3)->apply(5)->get(); + + $this->assertEquals(10,$res); + } +} \ No newline at end of file