Compare commits
	
		
			No commits in common. "3master" and "2master" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										4
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						|  | @ -2,13 +2,9 @@ | ||||||
| *.pyo | *.pyo | ||||||
| *.swp | *.swp | ||||||
| dist/ | dist/ | ||||||
| var/ |  | ||||||
| *.egg-info |  | ||||||
| *.project | *.project | ||||||
| *.pydevproject | *.pydevproject | ||||||
| *.ropeproject | *.ropeproject | ||||||
| *.sublime-project | *.sublime-project | ||||||
| *.sublime-workspace | *.sublime-workspace | ||||||
| .env |  | ||||||
| .settings | .settings | ||||||
| adminuser.zcml |  | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								LICENSE
									
										
									
									
									
								
							
							
						
						|  | @ -1,21 +0,0 @@ | ||||||
| The MIT License (MIT)  |  | ||||||
| 
 |  | ||||||
| Copyright (C) 2023 cyberconcepts.org team |  | ||||||
| 
 |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
| of this software and associated documentation files (the "Software"), to deal |  | ||||||
| in the Software without restriction, including without limitation the rights |  | ||||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
| copies of the Software, and to permit persons to whom the Software is |  | ||||||
| furnished to do so, subject to the following conditions: |  | ||||||
| 
 |  | ||||||
| The above copyright notice and this permission notice shall be included in all |  | ||||||
| copies or substantial portions of the Software. |  | ||||||
| 
 |  | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |  | ||||||
| SOFTWARE. |  | ||||||
							
								
								
									
										222
									
								
								LICENSE.GPL
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,222 @@ | ||||||
|  |                        GNU GENERAL PUBLIC LICENSE | ||||||
|  |    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||||
|  | 
 | ||||||
|  |   0. This License applies to any program or other work which contains | ||||||
|  | a notice placed by the copyright holder saying it may be distributed | ||||||
|  | under the terms of this General Public License.  The "Program", below, | ||||||
|  | refers to any such program or work, and a "work based on the Program" | ||||||
|  | means either the Program or any derivative work under copyright law: | ||||||
|  | that is to say, a work containing the Program or a portion of it, | ||||||
|  | either verbatim or with modifications and/or translated into another | ||||||
|  | language.  (Hereinafter, translation is included without limitation in | ||||||
|  | the term "modification".)  Each licensee is addressed as "you". | ||||||
|  | 
 | ||||||
|  | Activities other than copying, distribution and modification are not | ||||||
|  | covered by this License; they are outside its scope.  The act of | ||||||
|  | running the Program is not restricted, and the output from the Program | ||||||
|  | is covered only if its contents constitute a work based on the | ||||||
|  | Program (independent of having been made by running the Program). | ||||||
|  | Whether that is true depends on what the Program does. | ||||||
|  | 
 | ||||||
|  |   1. You may copy and distribute verbatim copies of the Program's | ||||||
|  | source code as you receive it, in any medium, provided that you | ||||||
|  | conspicuously and appropriately publish on each copy an appropriate | ||||||
|  | copyright notice and disclaimer of warranty; keep intact all the | ||||||
|  | notices that refer to this License and to the absence of any warranty; | ||||||
|  | and give any other recipients of the Program a copy of this License | ||||||
|  | along with the Program. | ||||||
|  | 
 | ||||||
|  | You may charge a fee for the physical act of transferring a copy, and | ||||||
|  | you may at your option offer warranty protection in exchange for a fee. | ||||||
|  | 
 | ||||||
|  |   2. You may modify your copy or copies of the Program or any portion | ||||||
|  | of it, thus forming a work based on the Program, and copy and | ||||||
|  | distribute such modifications or work under the terms of Section 1 | ||||||
|  | above, provided that you also meet all of these conditions: | ||||||
|  | 
 | ||||||
|  |     a) You must cause the modified files to carry prominent notices | ||||||
|  |     stating that you changed the files and the date of any change. | ||||||
|  | 
 | ||||||
|  |     b) You must cause any work that you distribute or publish, that in | ||||||
|  |     whole or in part contains or is derived from the Program or any | ||||||
|  |     part thereof, to be licensed as a whole at no charge to all third | ||||||
|  |     parties under the terms of this License. | ||||||
|  | 
 | ||||||
|  |     c) If the modified program normally reads commands interactively | ||||||
|  |     when run, you must cause it, when started running for such | ||||||
|  |     interactive use in the most ordinary way, to print or display an | ||||||
|  |     announcement including an appropriate copyright notice and a | ||||||
|  |     notice that there is no warranty (or else, saying that you provide | ||||||
|  |     a warranty) and that users may redistribute the program under | ||||||
|  |     these conditions, and telling the user how to view a copy of this | ||||||
|  |     License.  (Exception: if the Program itself is interactive but | ||||||
|  |     does not normally print such an announcement, your work based on | ||||||
|  |     the Program is not required to print an announcement.) | ||||||
|  | 
 | ||||||
|  | These requirements apply to the modified work as a whole.  If | ||||||
|  | identifiable sections of that work are not derived from the Program, | ||||||
|  | and can be reasonably considered independent and separate works in | ||||||
|  | themselves, then this License, and its terms, do not apply to those | ||||||
|  | sections when you distribute them as separate works.  But when you | ||||||
|  | distribute the same sections as part of a whole which is a work based | ||||||
|  | on the Program, the distribution of the whole must be on the terms of | ||||||
|  | this License, whose permissions for other licensees extend to the | ||||||
|  | entire whole, and thus to each and every part regardless of who wrote it. | ||||||
|  | 
 | ||||||
|  | Thus, it is not the intent of this section to claim rights or contest | ||||||
|  | your rights to work written entirely by you; rather, the intent is to | ||||||
|  | exercise the right to control the distribution of derivative or | ||||||
|  | collective works based on the Program. | ||||||
|  | 
 | ||||||
|  | In addition, mere aggregation of another work not based on the Program | ||||||
|  | with the Program (or with a work based on the Program) on a volume of | ||||||
|  | a storage or distribution medium does not bring the other work under | ||||||
|  | the scope of this License. | ||||||
|  | 
 | ||||||
|  |   3. You may copy and distribute the Program (or a work based on it, | ||||||
|  | under Section 2) in object code or executable form under the terms of | ||||||
|  | Sections 1 and 2 above provided that you also do one of the following: | ||||||
|  | 
 | ||||||
|  |     a) Accompany it with the complete corresponding machine-readable | ||||||
|  |     source code, which must be distributed under the terms of Sections | ||||||
|  |     1 and 2 above on a medium customarily used for software interchange; or, | ||||||
|  | 
 | ||||||
|  |     b) Accompany it with a written offer, valid for at least three | ||||||
|  |     years, to give any third party, for a charge no more than your | ||||||
|  |     cost of physically performing source distribution, a complete | ||||||
|  |     machine-readable copy of the corresponding source code, to be | ||||||
|  |     distributed under the terms of Sections 1 and 2 above on a medium | ||||||
|  |     customarily used for software interchange; or, | ||||||
|  | 
 | ||||||
|  |     c) Accompany it with the information you received as to the offer | ||||||
|  |     to distribute corresponding source code.  (This alternative is | ||||||
|  |     allowed only for noncommercial distribution and only if you | ||||||
|  |     received the program in object code or executable form with such | ||||||
|  |     an offer, in accord with Subsection b above.) | ||||||
|  | 
 | ||||||
|  | The source code for a work means the preferred form of the work for | ||||||
|  | making modifications to it.  For an executable work, complete source | ||||||
|  | code means all the source code for all modules it contains, plus any | ||||||
|  | associated interface definition files, plus the scripts used to | ||||||
|  | control compilation and installation of the executable.  However, as a | ||||||
|  | special exception, the source code distributed need not include | ||||||
|  | anything that is normally distributed (in either source or binary | ||||||
|  | form) with the major components (compiler, kernel, and so on) of the | ||||||
|  | operating system on which the executable runs, unless that component | ||||||
|  | itself accompanies the executable. | ||||||
|  | 
 | ||||||
|  | If distribution of executable or object code is made by offering | ||||||
|  | access to copy from a designated place, then offering equivalent | ||||||
|  | access to copy the source code from the same place counts as | ||||||
|  | distribution of the source code, even though third parties are not | ||||||
|  | compelled to copy the source along with the object code. | ||||||
|  | 
 | ||||||
|  |   4. You may not copy, modify, sublicense, or distribute the Program | ||||||
|  | except as expressly provided under this License.  Any attempt | ||||||
|  | otherwise to copy, modify, sublicense or distribute the Program is | ||||||
|  | void, and will automatically terminate your rights under this License. | ||||||
|  | However, parties who have received copies, or rights, from you under | ||||||
|  | this License will not have their licenses terminated so long as such | ||||||
|  | parties remain in full compliance. | ||||||
|  | 
 | ||||||
|  |   5. You are not required to accept this License, since you have not | ||||||
|  | signed it.  However, nothing else grants you permission to modify or | ||||||
|  | distribute the Program or its derivative works.  These actions are | ||||||
|  | prohibited by law if you do not accept this License.  Therefore, by | ||||||
|  | modifying or distributing the Program (or any work based on the | ||||||
|  | Program), you indicate your acceptance of this License to do so, and | ||||||
|  | all its terms and conditions for copying, distributing or modifying | ||||||
|  | the Program or works based on it. | ||||||
|  | 
 | ||||||
|  |   6. Each time you redistribute the Program (or any work based on the | ||||||
|  | Program), the recipient automatically receives a license from the | ||||||
|  | original licensor to copy, distribute or modify the Program subject to | ||||||
|  | these terms and conditions.  You may not impose any further | ||||||
|  | restrictions on the recipients' exercise of the rights granted herein. | ||||||
|  | You are not responsible for enforcing compliance by third parties to | ||||||
|  | this License. | ||||||
|  | 
 | ||||||
|  |   7. If, as a consequence of a court judgment or allegation of patent | ||||||
|  | infringement or for any other reason (not limited to patent issues), | ||||||
|  | conditions are imposed on you (whether by court order, agreement or | ||||||
|  | otherwise) that contradict the conditions of this License, they do not | ||||||
|  | excuse you from the conditions of this License.  If you cannot | ||||||
|  | distribute so as to satisfy simultaneously your obligations under this | ||||||
|  | License and any other pertinent obligations, then as a consequence you | ||||||
|  | may not distribute the Program at all.  For example, if a patent | ||||||
|  | license would not permit royalty-free redistribution of the Program by | ||||||
|  | all those who receive copies directly or indirectly through you, then | ||||||
|  | the only way you could satisfy both it and this License would be to | ||||||
|  | refrain entirely from distribution of the Program. | ||||||
|  | 
 | ||||||
|  | If any portion of this section is held invalid or unenforceable under | ||||||
|  | any particular circumstance, the balance of the section is intended to | ||||||
|  | apply and the section as a whole is intended to apply in other | ||||||
|  | circumstances. | ||||||
|  | 
 | ||||||
|  | It is not the purpose of this section to induce you to infringe any | ||||||
|  | patents or other property right claims or to contest validity of any | ||||||
|  | such claims; this section has the sole purpose of protecting the | ||||||
|  | integrity of the free software distribution system, which is | ||||||
|  | implemented by public license practices.  Many people have made | ||||||
|  | generous contributions to the wide range of software distributed | ||||||
|  | through that system in reliance on consistent application of that | ||||||
|  | system; it is up to the author/donor to decide if he or she is willing | ||||||
|  | to distribute software through any other system and a licensee cannot | ||||||
|  | impose that choice. | ||||||
|  | 
 | ||||||
|  | This section is intended to make thoroughly clear what is believed to | ||||||
|  | be a consequence of the rest of this License. | ||||||
|  | 
 | ||||||
|  |   8. If the distribution and/or use of the Program is restricted in | ||||||
|  | certain countries either by patents or by copyrighted interfaces, the | ||||||
|  | original copyright holder who places the Program under this License | ||||||
|  | may add an explicit geographical distribution limitation excluding | ||||||
|  | those countries, so that distribution is permitted only in or among | ||||||
|  | countries not thus excluded.  In such case, this License incorporates | ||||||
|  | the limitation as if written in the body of this License. | ||||||
|  | 
 | ||||||
|  |   9. The Free Software Foundation may publish revised and/or new versions | ||||||
|  | of the General Public License from time to time.  Such new versions will | ||||||
|  | be similar in spirit to the present version, but may differ in detail to | ||||||
|  | address new problems or concerns. | ||||||
|  | 
 | ||||||
|  | Each version is given a distinguishing version number.  If the Program | ||||||
|  | specifies a version number of this License which applies to it and "any | ||||||
|  | later version", you have the option of following the terms and conditions | ||||||
|  | either of that version or of any later version published by the Free | ||||||
|  | Software Foundation.  If the Program does not specify a version number of | ||||||
|  | this License, you may choose any version ever published by the Free Software | ||||||
|  | Foundation. | ||||||
|  | 
 | ||||||
|  |   10. If you wish to incorporate parts of the Program into other free | ||||||
|  | programs whose distribution conditions are different, write to the author | ||||||
|  | to ask for permission.  For software which is copyrighted by the Free | ||||||
|  | Software Foundation, write to the Free Software Foundation; we sometimes | ||||||
|  | make exceptions for this.  Our decision will be guided by the two goals | ||||||
|  | of preserving the free status of all derivatives of our free software and | ||||||
|  | of promoting the sharing and reuse of software generally. | ||||||
|  | 
 | ||||||
|  |                             NO WARRANTY | ||||||
|  | 
 | ||||||
|  |   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||||
|  | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN | ||||||
|  | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||||||
|  | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||||||
|  | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS | ||||||
|  | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE | ||||||
|  | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||||||
|  | REPAIR OR CORRECTION. | ||||||
|  | 
 | ||||||
|  |   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||||
|  | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||||||
|  | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||||||
|  | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||||||
|  | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||||||
|  | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||||||
|  | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||||||
|  | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||||||
|  | POSSIBILITY OF SUCH DAMAGES. | ||||||
|  | 
 | ||||||
|  |                      END OF TERMS AND CONDITIONS | ||||||
|  | @ -48,14 +48,14 @@ top-level loops container and a concept manager: | ||||||
|   >>> cc1 = Concept() |   >>> cc1 = Concept() | ||||||
|   >>> concepts['cc1'] = cc1 |   >>> concepts['cc1'] = cc1 | ||||||
|   >>> cc1.title |   >>> cc1.title | ||||||
|   '' |   u'' | ||||||
|   >>> loopsRoot.getLoopsUri(cc1) |   >>> loopsRoot.getLoopsUri(cc1) | ||||||
|   '.loops/concepts/cc1' |   u'.loops/concepts/cc1' | ||||||
| 
 | 
 | ||||||
|   >>> cc2 = Concept('Zope 3') |   >>> cc2 = Concept(u'Zope 3') | ||||||
|   >>> concepts['cc2'] = cc2 |   >>> concepts['cc2'] = cc2 | ||||||
|   >>> cc2.title |   >>> cc2.title | ||||||
|   'Zope 3' |   u'Zope 3' | ||||||
| 
 | 
 | ||||||
| Now we want to relate the second concept to the first one. | Now we want to relate the second concept to the first one. | ||||||
| 
 | 
 | ||||||
|  | @ -73,11 +73,11 @@ ConceptRelation): | ||||||
| We can now ask our concepts for their related child and parent concepts: | We can now ask our concepts for their related child and parent concepts: | ||||||
| 
 | 
 | ||||||
|   >>> [getName(c) for c in cc1.getChildren()] |   >>> [getName(c) for c in cc1.getChildren()] | ||||||
|   ['cc2'] |   [u'cc2'] | ||||||
|   >>> len(cc1.getParents()) |   >>> len(cc1.getParents()) | ||||||
|   0 |   0 | ||||||
|   >>> [getName(p) for p in cc2.getParents()] |   >>> [getName(p) for p in cc2.getParents()] | ||||||
|   ['cc1'] |   [u'cc1'] | ||||||
| 
 | 
 | ||||||
|   >>> len(cc2.getChildren()) |   >>> len(cc2.getChildren()) | ||||||
|   0 |   0 | ||||||
|  | @ -90,24 +90,24 @@ a special predicate 'hasType'. | ||||||
|   >>> typeObject = concepts['type'] |   >>> typeObject = concepts['type'] | ||||||
|   >>> typeObject.setConceptType(typeObject) |   >>> typeObject.setConceptType(typeObject) | ||||||
|   >>> typeObject.getConceptType().title |   >>> typeObject.getConceptType().title | ||||||
|   'Type' |   u'Type' | ||||||
| 
 | 
 | ||||||
|   >>> concepts['unknown'] = Concept('Unknown Type') |   >>> concepts['unknown'] = Concept(u'Unknown Type') | ||||||
|   >>> unknown = concepts['unknown'] |   >>> unknown = concepts['unknown'] | ||||||
|   >>> unknown.setConceptType(typeObject) |   >>> unknown.setConceptType(typeObject) | ||||||
|   >>> unknown.getConceptType().title |   >>> unknown.getConceptType().title | ||||||
|   'Type' |   u'Type' | ||||||
| 
 | 
 | ||||||
|   >>> cc1.setConceptType(unknown) |   >>> cc1.setConceptType(unknown) | ||||||
|   >>> cc1.getConceptType().title |   >>> cc1.getConceptType().title | ||||||
|   'Unknown Type' |   u'Unknown Type' | ||||||
| 
 | 
 | ||||||
|   >>> concepts['topic'] = Concept('Topic') |   >>> concepts['topic'] = Concept(u'Topic') | ||||||
|   >>> topic = concepts['topic'] |   >>> topic = concepts['topic'] | ||||||
|   >>> topic.setConceptType(typeObject) |   >>> topic.setConceptType(typeObject) | ||||||
|   >>> cc1.setConceptType(topic) |   >>> cc1.setConceptType(topic) | ||||||
|   >>> cc1.getConceptType().title |   >>> cc1.getConceptType().title | ||||||
|   'Topic' |   u'Topic' | ||||||
| 
 | 
 | ||||||
| We get a list of types using the ConceptTypeSourceList. | We get a list of types using the ConceptTypeSourceList. | ||||||
| In order for the type machinery to work we first have to provide a | In order for the type machinery to work we first have to provide a | ||||||
|  | @ -124,7 +124,7 @@ type manager. | ||||||
|   >>> from loops.concept import ConceptTypeSourceList |   >>> from loops.concept import ConceptTypeSourceList | ||||||
|   >>> types = ConceptTypeSourceList(cc1) |   >>> types = ConceptTypeSourceList(cc1) | ||||||
|   >>> sorted(t.title for t in types) |   >>> sorted(t.title for t in types) | ||||||
|   ['Customer', 'Domain', 'Predicate', 'Topic', 'Type', 'Unknown Type'] |   [u'Customer', u'Domain', u'Predicate', u'Topic', u'Type', u'Unknown Type'] | ||||||
| 
 | 
 | ||||||
| Using a PredicateSourceList we can retrieve a list of the available | Using a PredicateSourceList we can retrieve a list of the available | ||||||
| predicates. | predicates. | ||||||
|  | @ -136,7 +136,7 @@ Note that the 'hasType' predicate is suppressed from this list as the | ||||||
| corresponding relation is only assigned via the conceptType attribute: | corresponding relation is only assigned via the conceptType attribute: | ||||||
| 
 | 
 | ||||||
|   >>> sorted(t.title for t in predicates) |   >>> sorted(t.title for t in predicates) | ||||||
|   ['subobject'] |   [u'subobject'] | ||||||
| 
 | 
 | ||||||
| Concept Views | Concept Views | ||||||
| ------------- | ------------- | ||||||
|  | @ -146,7 +146,7 @@ Concept Views | ||||||
| 
 | 
 | ||||||
|   >>> children = list(view.children()) |   >>> children = list(view.children()) | ||||||
|   >>> [c.title for c in children] |   >>> [c.title for c in children] | ||||||
|   ['Zope 3'] |   [u'Zope 3'] | ||||||
| 
 | 
 | ||||||
| The token attribute provided with the items returned by the children() and | The token attribute provided with the items returned by the children() and | ||||||
| parents() methods identifies identifies not only the item itself but | parents() methods identifies identifies not only the item itself but | ||||||
|  | @ -154,19 +154,19 @@ also the relationship to the context object using a combination | ||||||
| of URIs to item and the predicate of the relationship: | of URIs to item and the predicate of the relationship: | ||||||
| 
 | 
 | ||||||
|   >>> [c.token for c in children] |   >>> [c.token for c in children] | ||||||
|   ['.loops/concepts/cc2:.loops/concepts/standard'] |   [u'.loops/concepts/cc2:.loops/concepts/standard'] | ||||||
| 
 | 
 | ||||||
| There is also a concept configuration view that allows updating the | There is also a concept configuration view that allows updating the | ||||||
| underlying context object: | underlying context object: | ||||||
| 
 | 
 | ||||||
|   >>> cc3 = Concept('loops for Zope 3') |   >>> cc3 = Concept(u'loops for Zope 3') | ||||||
|   >>> concepts['cc3'] = cc3 |   >>> concepts['cc3'] = cc3 | ||||||
|   >>> view = ConceptConfigureView(cc1, |   >>> view = ConceptConfigureView(cc1, | ||||||
|   ...           TestRequest(action='assign', tokens=['.loops/concepts/cc3'])) |   ...           TestRequest(action='assign', tokens=['.loops/concepts/cc3'])) | ||||||
|   >>> view.update() |   >>> view.update() | ||||||
|   True |   True | ||||||
|   >>> sorted(c.title for c in cc1.getChildren()) |   >>> sorted(c.title for c in cc1.getChildren()) | ||||||
|   ['Zope 3', 'loops for Zope 3'] |   [u'Zope 3', u'loops for Zope 3'] | ||||||
| 
 | 
 | ||||||
|   >>> input = {'action': 'remove', 'qualifier': 'children', |   >>> input = {'action': 'remove', 'qualifier': 'children', | ||||||
|   ...          'form.button.submit': 'Remove Chiildren', |   ...          'form.button.submit': 'Remove Chiildren', | ||||||
|  | @ -175,18 +175,18 @@ underlying context object: | ||||||
|   >>> view.update() |   >>> view.update() | ||||||
|   True |   True | ||||||
|   >>> sorted(c.title for c in cc1.getChildren()) |   >>> sorted(c.title for c in cc1.getChildren()) | ||||||
|   ['loops for Zope 3'] |   [u'loops for Zope 3'] | ||||||
| 
 | 
 | ||||||
| We can also create a new concept and assign it. | We can also create a new concept and assign it. | ||||||
| 
 | 
 | ||||||
|   >>> params = {'action': 'create', 'create.name': 'cc4', |   >>> params = {'action': 'create', 'create.name': 'cc4', | ||||||
|   ...           'create.title': 'New concept', |   ...           'create.title': u'New concept', | ||||||
|   ...           'create.type': '.loops/concepts/topic'} |   ...           'create.type': '.loops/concepts/topic'} | ||||||
|   >>> view = ConceptConfigureView(cc1, TestRequest(**params)) |   >>> view = ConceptConfigureView(cc1, TestRequest(**params)) | ||||||
|   >>> view.update() |   >>> view.update() | ||||||
|   True |   True | ||||||
|   >>> sorted(c.title for c in cc1.getChildren()) |   >>> sorted(c.title for c in cc1.getChildren()) | ||||||
|   ['New concept', 'loops for Zope 3'] |   [u'New concept', u'loops for Zope 3'] | ||||||
| 
 | 
 | ||||||
| The concept configuration view provides methods for displaying concept | The concept configuration view provides methods for displaying concept | ||||||
| types and predicates. | types and predicates. | ||||||
|  | @ -198,15 +198,15 @@ types and predicates. | ||||||
|   >>> component.provideAdapter(LoopsTerms, (IIterableSource, IBrowserRequest), ITerms) |   >>> component.provideAdapter(LoopsTerms, (IIterableSource, IBrowserRequest), ITerms) | ||||||
| 
 | 
 | ||||||
|   >>> sorted((t.title, t.token) for t in view.conceptTypes()) |   >>> sorted((t.title, t.token) for t in view.conceptTypes()) | ||||||
|   [('Customer', '.loops/concepts/customer'), |   [(u'Customer', '.loops/concepts/customer'), | ||||||
|    ('Domain', '.loops/concepts/domain'), |    (u'Domain', '.loops/concepts/domain'), | ||||||
|    ('Predicate', '.loops/concepts/predicate'), |    (u'Predicate', '.loops/concepts/predicate'), | ||||||
|    ('Topic', '.loops/concepts/topic'), |    (u'Topic', '.loops/concepts/topic'), | ||||||
|    ('Type', '.loops/concepts/type'), |    (u'Type', '.loops/concepts/type'), | ||||||
|    ('Unknown Type', '.loops/concepts/unknown')] |    (u'Unknown Type', '.loops/concepts/unknown')] | ||||||
| 
 | 
 | ||||||
|   >>> sorted((t.title, t.token) for t in view.predicates()) |   >>> sorted((t.title, t.token) for t in view.predicates()) | ||||||
|   [('subobject', '.loops/concepts/standard')] |   [(u'subobject', '.loops/concepts/standard')] | ||||||
| 
 | 
 | ||||||
| Index attributes adapter | Index attributes adapter | ||||||
| ------------------------ | ------------------------ | ||||||
|  | @ -214,10 +214,10 @@ Index attributes adapter | ||||||
|   >>> from loops.concept import IndexAttributes |   >>> from loops.concept import IndexAttributes | ||||||
|   >>> idx = IndexAttributes(cc2) |   >>> idx = IndexAttributes(cc2) | ||||||
|   >>> idx.text() |   >>> idx.text() | ||||||
|   'cc2 Zope 3' |   u'cc2 Zope 3' | ||||||
| 
 | 
 | ||||||
|   >>> idx.title() |   >>> idx.title() | ||||||
|   'cc2 Zope 3' |   u'cc2 Zope 3' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Resources and what they have to do with Concepts | Resources and what they have to do with Concepts | ||||||
|  | @ -233,27 +233,27 @@ A common type of resource is a document: | ||||||
| 
 | 
 | ||||||
|   >>> from loops.interfaces import IDocument |   >>> from loops.interfaces import IDocument | ||||||
|   >>> from loops.resource import Document |   >>> from loops.resource import Document | ||||||
|   >>> doc1 = Document('Zope Info') |   >>> doc1 = Document(u'Zope Info') | ||||||
|   >>> resources['doc1'] = doc1 |   >>> resources['doc1'] = doc1 | ||||||
|   >>> doc1.title |   >>> doc1.title | ||||||
|   'Zope Info' |   u'Zope Info' | ||||||
|   >>> doc1.data |   >>> doc1.data | ||||||
|   '' |   u'' | ||||||
|   >>> doc1.contentType |   >>> doc1.contentType | ||||||
|   '' |   u'' | ||||||
| 
 | 
 | ||||||
| We can also directly use Resource objects; these behave like files. | We can also directly use Resource objects; these behave like files. | ||||||
| In fact, by using resource types we can explicitly assign a resource | In fact, by using resource types we can explicitly assign a resource | ||||||
| the 'file' type, but we will use this feature later: | the 'file' type, but we will use this feature later: | ||||||
| 
 | 
 | ||||||
|   >>> img = Resource('A png Image') |   >>> img = Resource(u'A png Image') | ||||||
| 
 | 
 | ||||||
| For testing we use some simple files from the tests directory: | For testing we use some simple files from the tests directory: | ||||||
| 
 | 
 | ||||||
|   >>> from loops import tests |   >>> from loops import tests | ||||||
|   >>> import os |   >>> import os | ||||||
|   >>> path = os.path.join(*tests.__path__) |   >>> path = os.path.join(*tests.__path__) | ||||||
|   >>> img.data = open(os.path.join(path, 'test_icon.png'), 'rb').read() |   >>> img.data = open(os.path.join(path, 'test_icon.png')).read() | ||||||
|   >>> img.getSize() |   >>> img.getSize() | ||||||
|   381 |   381 | ||||||
|   >>> img.getImageSize() |   >>> img.getImageSize() | ||||||
|  | @ -261,8 +261,8 @@ For testing we use some simple files from the tests directory: | ||||||
|   >>> img.contentType |   >>> img.contentType | ||||||
|   'image/png' |   'image/png' | ||||||
| 
 | 
 | ||||||
|   >>> pdf = Resource('A pdf File') |   >>> pdf = Resource(u'A pdf File') | ||||||
|   >>> pdf.data = open(os.path.join(path, 'test.pdf'), 'rb').read() |   >>> pdf.data = open(os.path.join(path, 'test.pdf')).read() | ||||||
|   >>> pdf.getSize() |   >>> pdf.getSize() | ||||||
|   25862 |   25862 | ||||||
|   >>> pdf.getImageSize() |   >>> pdf.getImageSize() | ||||||
|  | @ -287,7 +287,7 @@ from concepts to resources: | ||||||
|   ...         'tokens': ['.loops/resources/doc1:.loops/concepts/standard']} |   ...         'tokens': ['.loops/resources/doc1:.loops/concepts/standard']} | ||||||
|   >>> view = ConceptConfigureView(cc1, TestRequest(form=form)) |   >>> view = ConceptConfigureView(cc1, TestRequest(form=form)) | ||||||
|   >>> [getName(r.context) for r in view.resources()] |   >>> [getName(r.context) for r in view.resources()] | ||||||
|   ['doc1'] |   [u'doc1'] | ||||||
|   >>> view.update() |   >>> view.update() | ||||||
|   True |   True | ||||||
|   >>> len(cc1.getResources()) |   >>> len(cc1.getResources()) | ||||||
|  | @ -316,10 +316,10 @@ Index attributes adapter | ||||||
|   >>> component.provideAdapter(FileAdapter, provides=IFile) |   >>> component.provideAdapter(FileAdapter, provides=IFile) | ||||||
|   >>> idx = IndexAttributes(doc1) |   >>> idx = IndexAttributes(doc1) | ||||||
|   >>> idx.text() |   >>> idx.text() | ||||||
|   '' |   u'' | ||||||
| 
 | 
 | ||||||
|   >>> idx.title() |   >>> idx.title() | ||||||
|   'doc1 Zope Info' |   u'doc1 Zope Info' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Views/Nodes: Menus, Menu Items, Listings, Pages, etc | Views/Nodes: Menus, Menu Items, Listings, Pages, etc | ||||||
|  | @ -343,14 +343,14 @@ The view manager has already been created during setup. | ||||||
| The view space is typically built up with nodes; a node may be a top-level | The view space is typically built up with nodes; a node may be a top-level | ||||||
| menu that may contain other nodes as menu or content items: | menu that may contain other nodes as menu or content items: | ||||||
| 
 | 
 | ||||||
|   >>> m1 = views['m1'] = Node('Menu') |   >>> m1 = views['m1'] = Node(u'Menu') | ||||||
|   >>> m11 = m1['m11'] = Node('Zope') |   >>> m11 = m1['m11'] = Node(u'Zope') | ||||||
|   >>> m111 = m11['m111'] = Node('Zope in General') |   >>> m111 = m11['m111'] = Node(u'Zope in General') | ||||||
|   >>> m112 = m11['m112'] = Node('Zope 3') |   >>> m112 = m11['m112'] = Node(u'Zope 3') | ||||||
|   >>> m112.title |   >>> m112.title | ||||||
|   'Zope 3' |   u'Zope 3' | ||||||
|   >>> m112.description |   >>> m112.description | ||||||
|   '' |   u'' | ||||||
| 
 | 
 | ||||||
| There are a few convienence methods for accessing parent and child nodes: | There are a few convienence methods for accessing parent and child nodes: | ||||||
| 
 | 
 | ||||||
|  | @ -359,7 +359,7 @@ There are a few convienence methods for accessing parent and child nodes: | ||||||
|   >>> m11.getParentNode() is m1 |   >>> m11.getParentNode() is m1 | ||||||
|   True |   True | ||||||
|   >>> [getName(child) for child in m11.getChildNodes()] |   >>> [getName(child) for child in m11.getChildNodes()] | ||||||
|   ['m111', 'm112'] |   [u'm111', u'm112'] | ||||||
| 
 | 
 | ||||||
| What is returned by these may be controlled by the nodeType attribute: | What is returned by these may be controlled by the nodeType attribute: | ||||||
| 
 | 
 | ||||||
|  | @ -444,13 +444,13 @@ Node Views | ||||||
|   >>> page = view.page |   >>> page = view.page | ||||||
|   >>> items = page.textItems |   >>> items = page.textItems | ||||||
|   >>> for item in items: |   >>> for item in items: | ||||||
|   ...     print(item.url, item.editable) |   ...     print item.url, item.editable | ||||||
|   http://127.0.0.1/loops/views/m1/m11/m112 False |   http://127.0.0.1/loops/views/m1/m11/m112 False | ||||||
| 
 | 
 | ||||||
|   >>> menu = view.menu |   >>> menu = view.menu | ||||||
|   >>> items = menu.menuItems |   >>> items = menu.menuItems | ||||||
|   >>> for item in items: |   >>> for item in items: | ||||||
|   ...     print(item.url, view.selected(item)) |   ...     print item.url, view.selected(item) | ||||||
|   http://127.0.0.1/loops/views/m1/m11 True |   http://127.0.0.1/loops/views/m1/m11 True | ||||||
| 
 | 
 | ||||||
| A NodeView provides an itemNum attribute that may be used to count elements | A NodeView provides an itemNum attribute that may be used to count elements | ||||||
|  | @ -493,14 +493,14 @@ view; these views we have to provide as multi-adapters: | ||||||
|   >>> len(tt) |   >>> len(tt) | ||||||
|   9 |   9 | ||||||
|   >>> sorted((t.token, t.title) for t in view.targetTypes())[1] |   >>> sorted((t.token, t.title) for t in view.targetTypes())[1] | ||||||
|   ('.loops/concepts/domain', 'Domain') |   ('.loops/concepts/domain', u'Domain') | ||||||
|   >>> view.update() |   >>> view.update() | ||||||
|   True |   True | ||||||
|   >>> sorted(resources.keys()) |   >>> sorted(resources.keys()) | ||||||
|   ['d001.txt', 'd002.txt', 'd003.txt', 'doc1', 'm1.m11.m111'] |   [u'd001.txt', u'd002.txt', u'd003.txt', u'doc1', u'm1.m11.m111'] | ||||||
| 
 | 
 | ||||||
|   >>> view.target.title, view.target.token |   >>> view.target.title, view.target.token | ||||||
|   ('New Resource', '.loops/resources/m1.m11.m111') |   ('New Resource', u'.loops/resources/m1.m11.m111') | ||||||
| 
 | 
 | ||||||
| A node object provides the targetSchema of its target: | A node object provides the targetSchema of its target: | ||||||
| 
 | 
 | ||||||
|  | @ -537,28 +537,28 @@ view for rendering.) | ||||||
|   >>> component.provideAdapter(LoopsType) |   >>> component.provideAdapter(LoopsType) | ||||||
|   >>> view = NodeView(m112, TestRequest()) |   >>> view = NodeView(m112, TestRequest()) | ||||||
|   >>> view.renderTarget() |   >>> view.renderTarget() | ||||||
|   '<pre></pre>' |   u'<pre></pre>' | ||||||
|   >>> doc1.data = 'Test data\n\nAnother paragraph' |   >>> doc1.data = u'Test data\n\nAnother paragraph' | ||||||
|   >>> view.renderTarget() |   >>> view.renderTarget() | ||||||
|   '<pre>Test data\n\nAnother paragraph</pre>' |   u'<pre>Test data\n\nAnother paragraph</pre>' | ||||||
| 
 | 
 | ||||||
|   >>> doc1.contentType = 'text/restructured' |   >>> doc1.contentType = 'text/restructured' | ||||||
|   >>> doc1.data = 'Test data\n\nAnother `paragraph <para>`_' |   >>> doc1.data = u'Test data\n\nAnother `paragraph <para>`_' | ||||||
| 
 | 
 | ||||||
|   >>> from loops.wiki.base import wikiLinksActive |   >>> from loops.wiki.base import wikiLinksActive | ||||||
|   >>> wikiLinksActive(loopsRoot) |   >>> wikiLinksActive(loopsRoot) | ||||||
|   False |   False | ||||||
| 
 | 
 | ||||||
|   >>> view.renderTarget() |   >>> view.renderTarget() | ||||||
|   '<p>Test data</p>\n<p>Another <a class="reference external" href="para">paragraph</a></p>\n' |   u'<p>Test data</p>\n<p>Another <a class="reference external" href="para">paragraph</a></p>\n' | ||||||
| 
 | 
 | ||||||
| '<p>Test data</p>\n<p>Another <a class="reference create" | u'<p>Test data</p>\n<p>Another <a class="reference create" | ||||||
|     href="http://127.0.0.1/loops/wiki/create.html?linkid=0000001">?paragraph</a></p>\n' |     href="http://127.0.0.1/loops/wiki/create.html?linkid=0000001">?paragraph</a></p>\n' | ||||||
| 
 | 
 | ||||||
|   >>> #links = loopsRoot.getRecordManager()['links'] |   >>> #links = loopsRoot.getRecordManager()['links'] | ||||||
|   >>> #links['0000001'] |   >>> #links['0000001'] | ||||||
| 
 | 
 | ||||||
| <Link ['42', 1, '', '... ...', 'para', None]: {}> | <Link ['42', 1, '', '... ...', u'para', None]: {}> | ||||||
| 
 | 
 | ||||||
| If the target object is removed from its container all references | If the target object is removed from its container all references | ||||||
| to it are removed as well. (To make this work we have to handle | to it are removed as well. (To make this work we have to handle | ||||||
|  | @ -632,7 +632,7 @@ Let's add some more nodes and reorder them: | ||||||
|   >>> m114 = Node() |   >>> m114 = Node() | ||||||
|   >>> m11['m114'] = m114 |   >>> m11['m114'] = m114 | ||||||
|   >>> m11.keys() |   >>> m11.keys() | ||||||
|   ['m111', 'm112', 'm113', 'm114'] |   [u'm111', u'm112', u'm113', u'm114'] | ||||||
| 
 | 
 | ||||||
| A special management view provides methods for moving objects down, up, | A special management view provides methods for moving objects down, up, | ||||||
| to the bottom, and to the top. | to the bottom, and to the top. | ||||||
|  | @ -641,10 +641,10 @@ to the bottom, and to the top. | ||||||
|   >>> view = OrderedContainerView(m11, TestRequest()) |   >>> view = OrderedContainerView(m11, TestRequest()) | ||||||
|   >>> view.move_bottom(('m113',)) |   >>> view.move_bottom(('m113',)) | ||||||
|   >>> m11.keys() |   >>> m11.keys() | ||||||
|   ['m111', 'm112', 'm114', 'm113'] |   [u'm111', u'm112', u'm114', u'm113'] | ||||||
|   >>> view.move_up(('m114',), 1) |   >>> view.move_up(('m114',), 1) | ||||||
|   >>> m11.keys() |   >>> m11.keys() | ||||||
|   ['m111', 'm114', 'm112', 'm113'] |   [u'm111', u'm114', u'm112', u'm113'] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Breadcrumbs | Breadcrumbs | ||||||
|  | @ -661,9 +661,9 @@ Breadcrumbs | ||||||
|   >>> view = NodeView(m114, request) |   >>> view = NodeView(m114, request) | ||||||
|   >>> request.annotations.setdefault('loops.view', {})['nodeView'] = view |   >>> request.annotations.setdefault('loops.view', {})['nodeView'] = view | ||||||
|   >>> view.breadcrumbs() |   >>> view.breadcrumbs() | ||||||
|   [{'label': 'Menu', 'url': 'http://127.0.0.1/loops/views/m1'}, |   [{'url': 'http://127.0.0.1/loops/views/m1', 'label': u'Menu'}, | ||||||
|    {'label': 'Zope', 'url': 'http://127.0.0.1/loops/views/m1/m11'}, |    {'url': 'http://127.0.0.1/loops/views/m1/m11', 'label': u'Zope'}, | ||||||
|    {'label': '', 'url': 'http://127.0.0.1/loops/views/m1/m11/m114'}] |    {'url': 'http://127.0.0.1/loops/views/m1/m11/m114', 'label': u''}] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| End-user Forms and Special Views | End-user Forms and Special Views | ||||||
|  | @ -705,8 +705,8 @@ been created during setup. | ||||||
|   >>> custType = TypeConcept(customer) |   >>> custType = TypeConcept(customer) | ||||||
|   >>> custType.options |   >>> custType.options | ||||||
|   [] |   [] | ||||||
|   >>> cust1 = concepts['cust1'] = Concept('Zope Corporation') |   >>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation') | ||||||
|   >>> cust2 = concepts['cust2'] = Concept('cyberconcepts') |   >>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts') | ||||||
|   >>> for c in (cust1, cust2): c.conceptType = customer |   >>> for c in (cust1, cust2): c.conceptType = customer | ||||||
|   >>> custType.options = ('qualifier:assign',) |   >>> custType.options = ('qualifier:assign',) | ||||||
|   >>> ConceptType(cust1).qualifiers |   >>> ConceptType(cust1).qualifiers | ||||||
|  | @ -714,7 +714,7 @@ been created during setup. | ||||||
| 
 | 
 | ||||||
|   >>> form = CreateObjectForm(m112, TestRequest()) |   >>> form = CreateObjectForm(m112, TestRequest()) | ||||||
|   >>> form.presetTypesForAssignment |   >>> form.presetTypesForAssignment | ||||||
|   [{'title': 'Customer', 'token': 'loops:concept:customer'}] |   [{'token': 'loops:concept:customer', 'title': u'Customer'}] | ||||||
| 
 | 
 | ||||||
| If the node's target is a type concept we don't get any assignments because | If the node's target is a type concept we don't get any assignments because | ||||||
| it does not make much sense to assign resources or other concepts as | it does not make much sense to assign resources or other concepts as | ||||||
|  | @ -736,18 +736,18 @@ on data provided in this form: | ||||||
|   >>> note_tc = concepts['note'] |   >>> note_tc = concepts['note'] | ||||||
| 
 | 
 | ||||||
|   >>> component.provideAdapter(NameChooser) |   >>> component.provideAdapter(NameChooser) | ||||||
|   >>> request = TestRequest(form={'title': 'Test Note', |   >>> request = TestRequest(form={'title': u'Test Note', | ||||||
|   ...                             'form.type': '.loops/concepts/note', |   ...                             'form.type': u'.loops/concepts/note', | ||||||
|   ...                             'contentType': 'text/restructured', |   ...                             'contentType': u'text/restructured', | ||||||
|   ...                             'linkUrl': 'http://'}) |   ...                             'linkUrl': u'http://'}) | ||||||
|   >>> view = NodeView(m112, request) |   >>> view = NodeView(m112, request) | ||||||
|   >>> cont = CreateObject(view, request) |   >>> cont = CreateObject(view, request) | ||||||
|   >>> cont.update() |   >>> cont.update() | ||||||
|   False |   False | ||||||
|   >>> sorted(resources.keys()) |   >>> sorted(resources.keys()) | ||||||
|   [...'test_note'...] |   [...u'test_note'...] | ||||||
|   >>> resources['test_note'].title |   >>> resources['test_note'].title | ||||||
|   'Test Note' |   u'Test Note' | ||||||
| 
 | 
 | ||||||
| If there is a concept selected in the combo box we assign this to the newly | If there is a concept selected in the combo box we assign this to the newly | ||||||
| created object: | created object: | ||||||
|  | @ -755,8 +755,8 @@ created object: | ||||||
|   >>> from loops import util |   >>> from loops import util | ||||||
|   >>> topicUid = util.getUidForObject(topic) |   >>> topicUid = util.getUidForObject(topic) | ||||||
|   >>> predicateUid = util.getUidForObject(concepts.getDefaultPredicate()) |   >>> predicateUid = util.getUidForObject(concepts.getDefaultPredicate()) | ||||||
|   >>> request = TestRequest(form={'title': 'Test Note', |   >>> request = TestRequest(form={'title': u'Test Note', | ||||||
|   ...                             'form.type': '.loops/concepts/note', |   ...                             'form.type': u'.loops/concepts/note', | ||||||
|   ...                             'form.assignments.selected': |   ...                             'form.assignments.selected': | ||||||
|   ...                                   [':'.join((topicUid, predicateUid))]}) |   ...                                   [':'.join((topicUid, predicateUid))]}) | ||||||
|   >>> view = NodeView(m112, request) |   >>> view = NodeView(m112, request) | ||||||
|  | @ -764,22 +764,22 @@ created object: | ||||||
|   >>> cont.update() |   >>> cont.update() | ||||||
|   False |   False | ||||||
|   >>> sorted(resources.keys()) |   >>> sorted(resources.keys()) | ||||||
|   [...'test_note-2'...] |   [...u'test_note-2'...] | ||||||
|   >>> note = resources['test_note-2'] |   >>> note = resources['test_note-2'] | ||||||
|   >>> sorted(t.__name__ for t in note.getConcepts()) |   >>> sorted(t.__name__ for t in note.getConcepts()) | ||||||
|   ['note', 'topic'] |   [u'note', u'topic'] | ||||||
| 
 | 
 | ||||||
| When creating an object its name may be automatically generated using the title | When creating an object its name may be automatically generated using the title | ||||||
| of the object. Let's make sure that the name chooser also handles special | of the object. Let's make sure that the name chooser also handles special | ||||||
| and possibly critcal cases: | and possibly critcal cases: | ||||||
| 
 | 
 | ||||||
|   >>> nc = NameChooser(resources) |   >>> nc = NameChooser(resources) | ||||||
|   >>> nc.chooseName('', Resource('abc: (cde)')) |   >>> nc.chooseName(u'', Resource(u'abc: (cde)')) | ||||||
|   'abc__cde' |   u'abc__cde' | ||||||
|   >>> nc.chooseName('', Resource('\xdcml\xe4ut')) |   >>> nc.chooseName(u'', Resource(u'\xdcml\xe4ut')) | ||||||
|   'uemlaeut' |   u'uemlaeut' | ||||||
|   >>> nc.chooseName('', Resource('A very very loooooong title')) |   >>> nc.chooseName(u'', Resource(u'A very very loooooong title')) | ||||||
|   'a_title' |   u'a_title' | ||||||
| 
 | 
 | ||||||
| Editing an Object | Editing an Object | ||||||
| ----------------- | ----------------- | ||||||
|  | @ -796,7 +796,7 @@ that in turns calls formlibs ``setUpWidgets()``. | ||||||
| The new technique uses the ``fields`` and ``data`` attributes... | The new technique uses the ``fields`` and ``data`` attributes... | ||||||
| 
 | 
 | ||||||
|   >>> for f in view.fields: |   >>> for f in view.fields: | ||||||
|   ...     print(f.name, f.fieldType, f.required, f.vocabulary) |   ...     print f.name, f.fieldType, f.required, f.vocabulary | ||||||
|   title textline True None |   title textline True None | ||||||
|   data textarea False None |   data textarea False None | ||||||
|   contentType dropdown True <...SimpleVocabulary object...> |   contentType dropdown True <...SimpleVocabulary object...> | ||||||
|  | @ -804,22 +804,22 @@ The new technique uses the ``fields`` and ``data`` attributes... | ||||||
|   linkText textline False None |   linkText textline False None | ||||||
| 
 | 
 | ||||||
|   >>> view.data |   >>> view.data | ||||||
|   {'title': 'Test Note', 'data': '', 'contentType': 'text/restructured',  |   {'linkUrl': u'http://', 'contentType': u'text/restructured', 'data': u'', | ||||||
|    'linkUrl': 'http://', 'linkText': ''} |    'linkText': u'', 'title': u'Test Note'} | ||||||
| 
 | 
 | ||||||
| The object is changed via a FormController adapter created for | The object is changed via a FormController adapter created for | ||||||
| a NodeView. | a NodeView. | ||||||
| 
 | 
 | ||||||
|   >>> form = dict( |   >>> form = dict( | ||||||
|   ...     title='Test Note - changed', |   ...     title=u'Test Note - changed', | ||||||
|   ...     contentType='text/plain',) |   ...     contentType=u'text/plain',) | ||||||
|   >>> request = TestRequest(form=form) |   >>> request = TestRequest(form=form) | ||||||
|   >>> view = NodeView(m112, request) |   >>> view = NodeView(m112, request) | ||||||
|   >>> cont = EditObject(view, request) |   >>> cont = EditObject(view, request) | ||||||
|   >>> cont.update() |   >>> cont.update() | ||||||
|   False |   False | ||||||
|   >>> resources['test_note'].title |   >>> resources['test_note'].title | ||||||
|   'Test Note - changed' |   u'Test Note - changed' | ||||||
| 
 | 
 | ||||||
| Virtual Targets | Virtual Targets | ||||||
| --------------- | --------------- | ||||||
|  | @ -883,13 +883,13 @@ informations about all parents of an object. | ||||||
| 
 | 
 | ||||||
|   >>> parents = m113.getAllParents() |   >>> parents = m113.getAllParents() | ||||||
|   >>> for p in parents: |   >>> for p in parents: | ||||||
|   ...     print(p.object.title) |   ...     print p.object.title | ||||||
|   Zope |   Zope | ||||||
|   Menu |   Menu | ||||||
| 
 | 
 | ||||||
|   >>> parents = resources['test_note'].getAllParents() |   >>> parents = resources['test_note'].getAllParents() | ||||||
|   >>> for p in parents: |   >>> for p in parents: | ||||||
|   ...     print(p.object.title, len(p.relations)) |   ...     print p.object.title, len(p.relations) | ||||||
|   Note 1 |   Note 1 | ||||||
|   Type 2 |   Type 2 | ||||||
| 
 | 
 | ||||||
|  | @ -916,10 +916,10 @@ relates ISO country codes with the full name of the country. | ||||||
|   [('at', ['Austria']), ('de', ['Germany'])] |   [('at', ['Austria']), ('de', ['Germany'])] | ||||||
| 
 | 
 | ||||||
|   >>> countries.dataAsRecords() |   >>> countries.dataAsRecords() | ||||||
|   [{'key': 'at', 'value': 'Austria'}, {'key': 'de', 'value': 'Germany'}] |   [{'value': 'Austria', 'key': 'at'}, {'value': 'Germany', 'key': 'de'}] | ||||||
| 
 | 
 | ||||||
|   >>> countries.getRowsByValue('value', 'Germany') |   >>> countries.getRowsByValue('value', 'Germany') | ||||||
|   [{'key': 'de', 'value': 'Germany'}] |   [{'value': 'Germany', 'key': 'de'}] | ||||||
| 
 | 
 | ||||||
| The ``recordstable`` type is a variation of this datable type that contains | The ``recordstable`` type is a variation of this datable type that contains | ||||||
| a simple list of records - without a key column. A record in this  type is a | a simple list of records - without a key column. A record in this  type is a | ||||||
|  | @ -939,7 +939,7 @@ We just reuse the existing ``countries`` table and convert it to a records table | ||||||
|   >>> countries = adapted(concepts['countries']) |   >>> countries = adapted(concepts['countries']) | ||||||
| 
 | 
 | ||||||
|   >>> countries.data |   >>> countries.data | ||||||
|   [{'key': 'at', 'value': 'Austria'}, {'key': 'de', 'value': 'Germany'}] |   [{'value': 'Austria', 'key': 'at'}, {'value': 'Germany', 'key': 'de'}] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Caching | Caching | ||||||
|  | @ -951,7 +951,7 @@ To be done... | ||||||
|   >>> obj = resources['test_note'] |   >>> obj = resources['test_note'] | ||||||
|   >>> cxObj = cached(obj) |   >>> cxObj = cached(obj) | ||||||
|   >>> [p.object.title for p in cxObj.getAllParents()] |   >>> [p.object.title for p in cxObj.getAllParents()] | ||||||
|   ['Note', 'Type'] |   [u'Note', u'Type'] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Security | Security | ||||||
|  | @ -1,13 +1,30 @@ | ||||||
| # loops.base | # | ||||||
|  | #  Copyright (c) 2019 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Implementation of loops root object. | """ | ||||||
|  | Implementation of loops root object. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from zope.container.btree import BTreeContainer | from zope.app.container.btree import BTreeContainer | ||||||
| from zope.site.folder import Folder | from zope.app.folder.folder import Folder | ||||||
| from zope.site.interfaces import IFolder | from zope.app.folder.interfaces import IFolder | ||||||
| from zope.traversing.api import getPath, traverse | from zope.traversing.api import getPath, traverse | ||||||
| from zope.interface import implementer | from zope.interface import implements | ||||||
| 
 | 
 | ||||||
| from cybertools.util.jeep import Jeep | from cybertools.util.jeep import Jeep | ||||||
| from loops.interfaces import ILoops | from loops.interfaces import ILoops | ||||||
|  | @ -15,8 +32,17 @@ from loops.interfaces import ILoops | ||||||
| loopsPrefix = '.loops' | loopsPrefix = '.loops' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(ILoops) |  | ||||||
| class Loops(Folder): | class Loops(Folder): | ||||||
|  | #class Loops(BTreeContainer): | ||||||
|  | 
 | ||||||
|  |     implements(ILoops) | ||||||
|  | 
 | ||||||
|  |     #def getSiteManager(self): | ||||||
|  |     #    return self.__parent__.getSiteManager() | ||||||
|  | 
 | ||||||
|  |     #@property | ||||||
|  |     #def _SampleContainer__data(self): | ||||||
|  |     #    return self.data | ||||||
| 
 | 
 | ||||||
|     _skinName = '' |     _skinName = '' | ||||||
|     def getSkinName(self): return self._skinName |     def getSkinName(self): return self._skinName | ||||||
|  | @ -46,7 +72,10 @@ class Loops(Folder): | ||||||
|         return self.get('records') |         return self.get('records') | ||||||
| 
 | 
 | ||||||
|     def getLoopsUri(self, obj): |     def getLoopsUri(self, obj): | ||||||
|  |         #return str(loopsPrefix + getPath(obj)[len(getPath(self)):]) | ||||||
|         uri = loopsPrefix + getPath(obj)[len(getPath(self)):] |         uri = loopsPrefix + getPath(obj)[len(getPath(self)):] | ||||||
|  |         #if isinstance(uri, unicode): | ||||||
|  |         #    uri = uri.encode('UTF-8') | ||||||
|         return uri |         return uri | ||||||
| 
 | 
 | ||||||
|     def loopsTraverse(self, uri): |     def loopsTraverse(self, uri): | ||||||
|  | @ -1,9 +1,26 @@ | ||||||
| # loops.browser.action | # | ||||||
|  | #  Copyright (c) 2013 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Base classes (sort of views) for action portlet items. | """ | ||||||
|  | Base classes (sort of views) for action portlet items. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urllib.parse import urlencode | from urllib import urlencode | ||||||
| from zope import component | from zope import component | ||||||
| from zope.app.pagetemplate import ViewPageTemplateFile | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
|  | @ -1,18 +1,35 @@ | ||||||
| # loops.browser.auth | # | ||||||
|  | #  Copyright (c) 2015 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Login, logout, unauthorized stuff. | """ | ||||||
|  | Login, logout, unauthorized stuff. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from zope.app.exception.browser.unauthorized import Unauthorized as DefaultUnauth | from zope.app.exception.browser.unauthorized import Unauthorized as DefaultUnauth | ||||||
| from zope.authentication.interfaces import IAuthentication | from zope.app.security.interfaces import IAuthentication | ||||||
| from zope.authentication.interfaces import ILogout, IUnauthenticatedPrincipal | from zope.app.security.interfaces import ILogout, IUnauthenticatedPrincipal | ||||||
| from zope.browserpage import ViewPageTemplateFile |  | ||||||
| from zope.cachedescriptors.property import Lazy |  | ||||||
| from zope import component | from zope import component | ||||||
| from zope.interface import implementer | from zope.interface import implements | ||||||
| 
 | 
 | ||||||
| from loops.browser.concept import ConceptView | from loops.browser.concept import ConceptView | ||||||
| from loops.browser.node import NodeView | from loops.browser.node import NodeView | ||||||
|  | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
|  | from zope.cachedescriptors.property import Lazy | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| template = ViewPageTemplateFile('auth.pt') | template = ViewPageTemplateFile('auth.pt') | ||||||
|  | @ -40,9 +57,9 @@ class LoginForm(NodeView): | ||||||
|         return self |         return self | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(ILogout) |  | ||||||
| class Logout(object): | class Logout(object): | ||||||
| 
 | 
 | ||||||
|  |     implements(ILogout) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, context, request): |     def __init__(self, context, request): | ||||||
|         self.context = context |         self.context = context | ||||||
|  | @ -50,11 +67,10 @@ class Logout(object): | ||||||
| 
 | 
 | ||||||
|     def __call__(self): |     def __call__(self): | ||||||
|         nextUrl = self.request.get('nextURL') or self.request.URL[-1] |         nextUrl = self.request.get('nextURL') or self.request.URL[-1] | ||||||
|         nx = self.request.response.redirect(nextUrl) |  | ||||||
|         if not IUnauthenticatedPrincipal.providedBy(self.request.principal): |         if not IUnauthenticatedPrincipal.providedBy(self.request.principal): | ||||||
|             auth = component.getUtility(IAuthentication) |             auth = component.getUtility(IAuthentication) | ||||||
|             ILogout(auth).logout(self.request) |             ILogout(auth).logout(self.request) | ||||||
|         return nx |         return self.request.response.redirect(nextUrl) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Unauthorized(ConceptView): | class Unauthorized(ConceptView): | ||||||
|  | @ -1,28 +1,47 @@ | ||||||
| # loops.browser.common | # | ||||||
|  | #  Copyright (c) 2016 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Common base class for loops browser view classes. | """ | ||||||
|  | Common base class for loops browser view classes. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  | from cgi import parse_qsl | ||||||
| #import mimetypes   # use more specific assignments from cybertools.text | #import mimetypes   # use more specific assignments from cybertools.text | ||||||
| from datetime import date, datetime | from datetime import date, datetime | ||||||
| from logging import getLogger | from logging import getLogger | ||||||
| import re | import re | ||||||
| from time import strptime | from time import strptime | ||||||
| from urllib.parse import parse_qs, parse_qsl, urlencode | from urllib import urlencode | ||||||
|  | from urlparse import parse_qs | ||||||
| from zope import component | from zope import component | ||||||
| from zope.authentication.interfaces import IAuthentication, IUnauthenticatedPrincipal | from zope.app.form.browser.interfaces import ITerms | ||||||
| from zope.authentication.interfaces import IUnauthenticatedPrincipal | from zope.app.i18n.interfaces import ITranslationDomain | ||||||
| from zope.authentication.interfaces import PrincipalLookupError | from zope.app.security.interfaces import IAuthentication, IUnauthenticatedPrincipal | ||||||
| from zope.browser.interfaces import ITerms | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.browserpage import ViewPageTemplateFile | from zope.app.security.interfaces import IUnauthenticatedPrincipal | ||||||
|  | from zope.app.security.interfaces import PrincipalLookupError | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope.dottedname.resolve import resolve | from zope.dottedname.resolve import resolve | ||||||
| from zope.dublincore.interfaces import IZopeDublinCore | from zope.dublincore.interfaces import IZopeDublinCore | ||||||
| from zope.formlib import form | from zope.formlib import form | ||||||
| from zope.formlib.form import FormFields | from zope.formlib.form import FormFields | ||||||
| from zope.formlib.namedtemplate import NamedTemplate | from zope.formlib.namedtemplate import NamedTemplate | ||||||
| from zope.i18n.interfaces import ITranslationDomain | from zope.interface import Interface, implements | ||||||
| from zope.interface import Interface, implementer |  | ||||||
| from zope.proxy import removeAllProxies | from zope.proxy import removeAllProxies | ||||||
| from zope.publisher.browser import applySkin | from zope.publisher.browser import applySkin | ||||||
| from zope.publisher.interfaces.browser import IBrowserSkinType, IBrowserView | from zope.publisher.interfaces.browser import IBrowserSkinType, IBrowserView | ||||||
|  | @ -895,7 +914,6 @@ class BaseView(GenericView, I18NView, SortableMixin): | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def xeditable(self): |     def xeditable(self): | ||||||
|         return False |  | ||||||
|         if self.typeOptions('no_external_edit'): |         if self.typeOptions('no_external_edit'): | ||||||
|             return False |             return False | ||||||
|         ct = getattr(self.context, 'contentType', '') |         ct = getattr(self.context, 'contentType', '') | ||||||
|  | @ -1074,12 +1092,13 @@ class LoggedIn(object): | ||||||
| 
 | 
 | ||||||
| # vocabulary stuff | # vocabulary stuff | ||||||
| 
 | 
 | ||||||
| @implementer(ITerms) |  | ||||||
| class SimpleTerms(object): | class SimpleTerms(object): | ||||||
|     """ Provide the ITerms interface, e.g. for usage in selection |     """ Provide the ITerms interface, e.g. for usage in selection | ||||||
|         lists. |         lists. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  |     implements(ITerms) | ||||||
|  | 
 | ||||||
|     def __init__(self, source, request): |     def __init__(self, source, request): | ||||||
|         # the source parameter is a list of tuples (token, title). |         # the source parameter is a list of tuples (token, title). | ||||||
|         self.source = source |         self.source = source | ||||||
|  | @ -1094,12 +1113,13 @@ class SimpleTerms(object): | ||||||
|         return (token, self.terms[token]) |         return (token, self.terms[token]) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(ITerms) |  | ||||||
| class LoopsTerms(object): | class LoopsTerms(object): | ||||||
|     """ Provide the ITerms interface, e.g. for usage in selection |     """ Provide the ITerms interface, e.g. for usage in selection | ||||||
|         lists. |         lists. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  |     implements(ITerms) | ||||||
|  | 
 | ||||||
|     def __init__(self, source, request): |     def __init__(self, source, request): | ||||||
|         # the source parameter is a view or adapter of a real context object: |         # the source parameter is a view or adapter of a real context object: | ||||||
|         self.source = source |         self.source = source | ||||||
|  | @ -1121,11 +1141,12 @@ class LoopsTerms(object): | ||||||
|         return self.loopsRoot.loopsTraverse(token) |         return self.loopsRoot.loopsTraverse(token) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(ITerms) |  | ||||||
| class InterfaceTerms(object): | class InterfaceTerms(object): | ||||||
|     """ Provide the ITerms interface for source list of interfaces. |     """ Provide the ITerms interface for source list of interfaces. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  |     implements(ITerms) | ||||||
|  | 
 | ||||||
|     def __init__(self, source, request): |     def __init__(self, source, request): | ||||||
|         self.source = source |         self.source = source | ||||||
|         self.request = request |         self.request = request | ||||||
|  | @ -1,22 +1,40 @@ | ||||||
| # loops.browser.concept | # | ||||||
|  | #  Copyright (c) 2016 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Definition of the concept view classes. | """ | ||||||
|  | Definition of the concept view classes. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from itertools import groupby | from itertools import groupby | ||||||
| from zope import interface, component, schema | from zope import interface, component, schema | ||||||
| from zope.authentication.interfaces import IUnauthenticatedPrincipal | from zope.app.catalog.interfaces import ICatalog | ||||||
| from zope.browser.interfaces import ITerms | from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent | ||||||
| from zope.browserpage import ViewPageTemplateFile | from zope.app.container.contained import ObjectRemovedEvent | ||||||
|  | from zope.app.form.browser.interfaces import ITerms | ||||||
|  | from zope.app.form.interfaces import IDisplayWidget | ||||||
|  | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
|  | from zope.app.security.interfaces import IUnauthenticatedPrincipal | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope.catalog.interfaces import ICatalog |  | ||||||
| from zope.container.contained import ObjectRemovedEvent |  | ||||||
| from zope.dottedname.resolve import resolve | from zope.dottedname.resolve import resolve | ||||||
| from zope.event import notify | from zope.event import notify | ||||||
| from zope.formlib.form import EditForm, FormFields, setUpEditWidgets | from zope.formlib.form import EditForm, FormFields, setUpEditWidgets | ||||||
| from zope.formlib.interfaces import IDisplayWidget |  | ||||||
| from zope.formlib.namedtemplate import NamedTemplate | from zope.formlib.namedtemplate import NamedTemplate | ||||||
| from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent | from zope.interface import implements | ||||||
| from zope.publisher.interfaces import BadRequest | from zope.publisher.interfaces import BadRequest | ||||||
| from zope.publisher.interfaces.browser import IBrowserRequest | from zope.publisher.interfaces.browser import IBrowserRequest | ||||||
| from zope.schema.interfaces import IIterableSource | from zope.schema.interfaces import IIterableSource | ||||||
|  | @ -445,7 +463,7 @@ class ConceptView(BaseView): | ||||||
| 
 | 
 | ||||||
|     def parents(self): |     def parents(self): | ||||||
|         rels = sorted(self.context.getParentRelations(), |         rels = sorted(self.context.getParentRelations(), | ||||||
|                       key=(lambda x: x.first.title and x.first.title.lower() or '')) |                       key=(lambda x: x.first.title and x.first.title.lower())) | ||||||
|         for r in rels: |         for r in rels: | ||||||
|             yield self.childViewFactory(r, self.request) |             yield self.childViewFactory(r, self.request) | ||||||
| 
 | 
 | ||||||
|  | @ -1,8 +1,28 @@ | ||||||
| # loops.browser.external | # | ||||||
|  | #  Copyright (c) 2008 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ view class(es) for import/export. | """ | ||||||
|  | view class(es) for import/export. | ||||||
|  | 
 | ||||||
|  | $Id$ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
|  | from zope.interface import Interface, implements | ||||||
| from zope.app import zapi | from zope.app import zapi | ||||||
| from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile | from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB | 
|  | @ -1,18 +1,35 @@ | ||||||
| # loops.browser.form | # | ||||||
|  | #  Copyright (c) 2017 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Classes for form presentation and processing. | """ | ||||||
|  | Classes for form presentation and processing. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urllib.parse import urlencode, unquote_plus | from urllib import urlencode, unquote_plus | ||||||
|  | from zope.app.container.contained import ObjectRemovedEvent | ||||||
| from zope import component, interface, schema | from zope import component, interface, schema | ||||||
| from zope.component import adapts | from zope.component import adapts | ||||||
| from zope.container.contained import ObjectRemovedEvent |  | ||||||
| from zope.event import notify | from zope.event import notify | ||||||
| from zope.interface import Interface | from zope.interface import Interface | ||||||
| from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent | from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent | ||||||
| from zope.container.interfaces import INameChooser | from zope.app.container.interfaces import INameChooser | ||||||
| from zope.lifecycleevent import ObjectAddedEvent | from zope.app.container.contained import ObjectAddedEvent | ||||||
| from zope.browserpage import ViewPageTemplateFile | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope.contenttype import guess_content_type | from zope.contenttype import guess_content_type | ||||||
| from zope.publisher.browser import FileUpload | from zope.publisher.browser import FileUpload | ||||||
|  | @ -166,7 +183,7 @@ class ObjectForm(NodeView): | ||||||
|                 if field: |                 if field: | ||||||
|                     fi = field.getFieldInstance(self.instance) |                     fi = field.getFieldInstance(self.instance) | ||||||
|                     input = form[k] |                     input = form[k] | ||||||
|                     if isinstance(input, str): |                     if isinstance(input, basestring): | ||||||
|                         input = unquote_plus(input) |                         input = unquote_plus(input) | ||||||
|                     data[k] = fi.marshall(fi.unmarshall(input)) |                     data[k] = fi.marshall(fi.unmarshall(input)) | ||||||
|                     #data[k] = toUnicode(form[k]) |                     #data[k] = toUnicode(form[k]) | ||||||
|  | @ -1,9 +1,26 @@ | ||||||
| # loops.browser.lobo.standard | # | ||||||
|  | #  Copyright (c) 2012 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ View classes for lobo (blueprint-based) layouts. | """ | ||||||
|  | View classes for lobo (blueprint-based) layouts. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from urllib.parse import parse_qs | from cgi import parse_qs | ||||||
| from zope import interface, component | from zope import interface, component | ||||||
| from zope.app.pagetemplate import ViewPageTemplateFile | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| # loops.browser.lobo.tests |  | ||||||
| 
 | 
 | ||||||
| import unittest, doctest | import unittest, doctest | ||||||
| from zope.interface.verify import verifyClass | from zope.interface.verify import verifyClass | ||||||
|  | @ -13,7 +12,7 @@ class Test(unittest.TestCase): | ||||||
| def test_suite(): | def test_suite(): | ||||||
|     flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS |     flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | ||||||
|     return unittest.TestSuite(( |     return unittest.TestSuite(( | ||||||
|         unittest.TestLoader().loadTestsFromTestCase(Test), |                 unittest.makeSuite(Test), | ||||||
|                 doctest.DocFileSuite('README.txt', optionflags=flags), |                 doctest.DocFileSuite('README.txt', optionflags=flags), | ||||||
|             )) |             )) | ||||||
| 
 | 
 | ||||||
| Before Width: | Height: | Size: 942 B After Width: | Height: | Size: 942 B | 
|  | @ -1,11 +1,31 @@ | ||||||
| # loops.browser.mobile.default | # | ||||||
|  | #  Copyright (c) 2011 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Default layouts for the loops mobile skin. | """ | ||||||
|  | Default layouts for the loops mobile skin. | ||||||
|  | 
 | ||||||
|  | $Id$ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from zope.app.pagetemplate import ViewPageTemplateFile | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope import component | from zope import component | ||||||
|  | from zope.interface import implements | ||||||
| 
 | 
 | ||||||
| from cybertools.browser.renderer import RendererFactory | from cybertools.browser.renderer import RendererFactory | ||||||
| from cybertools.composer.layout.base import Layout | from cybertools.composer.layout.base import Layout | ||||||
|  | @ -1,20 +1,37 @@ | ||||||
| # loops.browser.node | # | ||||||
|  | #  Copyright (c) 2017 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ View class for Node objects. | """ | ||||||
|  | View class for Node objects. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from logging import getLogger | from logging import getLogger | ||||||
| from urllib.parse import urlencode, urlparse, urlunparse | import urllib | ||||||
| #from urlparse import urlparse, urlunparse | from urlparse import urlparse, urlunparse | ||||||
|  | from zope import component, interface, schema | ||||||
|  | from zope.cachedescriptors.property import Lazy | ||||||
|  | from zope.annotation.interfaces import IAnnotations | ||||||
|  | from zope.app.catalog.interfaces import ICatalog | ||||||
| from zope.app.container.browser.contents import JustContents | from zope.app.container.browser.contents import JustContents | ||||||
| from zope.app.container.browser.adding import Adding | from zope.app.container.browser.adding import Adding | ||||||
| from zope import component, interface, schema | from zope.app.container.traversal import ItemTraverser | ||||||
| from zope.annotation.interfaces import IAnnotations | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.authentication.interfaces import IUnauthenticatedPrincipal | from zope.app.security.interfaces import IUnauthenticatedPrincipal | ||||||
| from zope.browserpage import ViewPageTemplateFile |  | ||||||
| from zope.cachedescriptors.property import Lazy |  | ||||||
| from zope.catalog.interfaces import ICatalog |  | ||||||
| from zope.container.traversal import ItemTraverser |  | ||||||
| from zope.dottedname.resolve import resolve | from zope.dottedname.resolve import resolve | ||||||
| from zope.event import notify | from zope.event import notify | ||||||
| from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent | from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent | ||||||
|  | @ -462,7 +479,7 @@ class NodeView(BaseView): | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|     def logoutUrl(self): |     def logoutUrl(self): | ||||||
|         nextUrl = urlencode(dict(nextUrl=self.menu.url)) |         nextUrl = urllib.urlencode(dict(nextUrl=self.menu.url)) | ||||||
|         return '%s/logout.html?%s' % (self.menu.url, nextUrl) |         return '%s/logout.html?%s' % (self.menu.url, nextUrl) | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @Lazy | ||||||
|  | @ -769,11 +786,11 @@ class InlineEdit(NodeView): | ||||||
|         if ti is not None: |         if ti is not None: | ||||||
|             target = ti(target) |             target = ti(target) | ||||||
|         data = self.request.form['editorContent'] |         data = self.request.form['editorContent'] | ||||||
|         if not isinstance(data, str): |         if type(data) != unicode: | ||||||
|             try: |             try: | ||||||
|                 data = data.decode('ISO-8859-15')  # IE hack |                 data = data.decode('ISO-8859-15')  # IE hack | ||||||
|             except UnicodeDecodeError: |             except UnicodeDecodeError: | ||||||
|                 print('loops.browser.node.InlineEdit.save():', data) |                 print 'loops.browser.node.InlineEdit.save():', data | ||||||
|                 return |                 return | ||||||
|         #    data = data.decode('UTF-8') |         #    data = data.decode('UTF-8') | ||||||
|         target.data = data |         target.data = data | ||||||
|  | @ -945,9 +962,9 @@ class NodeAdding(Adding): | ||||||
|         return info |         return info | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @interface.implementer(IViewConfiguratorSchema) |  | ||||||
| class ViewPropertiesConfigurator(object): | class ViewPropertiesConfigurator(object): | ||||||
| 
 | 
 | ||||||
|  |     interface.implements(IViewConfiguratorSchema) | ||||||
|     component.adapts(INode) |     component.adapts(INode) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, context): |     def __init__(self, context): | ||||||
|  | @ -1035,7 +1052,7 @@ class NodeTraverser(ItemTraverser): | ||||||
|                 return self.context |                 return self.context | ||||||
|         try: |         try: | ||||||
|             obj = super(NodeTraverser, self).publishTraverse(request, name) |             obj = super(NodeTraverser, self).publishTraverse(request, name) | ||||||
|         except NotFound: |         except NotFound, e: | ||||||
|             logger.warn('NodeTraverser: NotFound: URL = %s, name = %r' % |             logger.warn('NodeTraverser: NotFound: URL = %s, name = %r' % | ||||||
|                             (request.URL, name)) |                             (request.URL, name)) | ||||||
|             raise |             raise | ||||||
|  | @ -1107,12 +1124,12 @@ def getViewConfiguration(context, request): | ||||||
| class TestView(NodeView): | class TestView(NodeView): | ||||||
| 
 | 
 | ||||||
|     def __call__(self): |     def __call__(self): | ||||||
|         print( '*** begin') |         print '*** begin' | ||||||
|         for i in range(500): |         for i in range(500): | ||||||
|             #x = util.getObjectForUid('1994729849') |             #x = util.getObjectForUid('1994729849') | ||||||
|             x = util.getObjectForUid('2018653366') |             x = util.getObjectForUid('2018653366') | ||||||
|             self.c = list(x.getChildren()) |             self.c = list(x.getChildren()) | ||||||
|             #self.c = list(x.getChildren([self.defaultPredicate])) |             #self.c = list(x.getChildren([self.defaultPredicate])) | ||||||
|         print('*** end', len(self.c)) |         print '*** end', len(self.c) | ||||||
|         return 'done' |         return 'done' | ||||||
| 
 | 
 | ||||||
| Before Width: | Height: | Size: 942 B After Width: | Height: | Size: 942 B | 
|  | @ -18,6 +18,10 @@ | ||||||
|           <a href="#" |           <a href="#" | ||||||
|              tal:attributes="href string:${target/url}/@@configure.html" |              tal:attributes="href string:${target/url}/@@configure.html" | ||||||
|              tal:content="target/title">Document xy</a> |              tal:content="target/title">Document xy</a> | ||||||
|  |           <tal:xedit define="xeditObjectUrl target/url" | ||||||
|  |                      condition="target/xeditable"> | ||||||
|  |             <metal:xedit use-macro="views/xedit_macros/editLink" /> | ||||||
|  |           </tal:xedit> | ||||||
|         </tal:target> |         </tal:target> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|  | @ -1,18 +1,36 @@ | ||||||
| # loops.browser.resource | # | ||||||
|  | #  Copyright (c) 2015 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ View class for resource objects. | """ | ||||||
|  | View class for resource objects. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import os.path | import os.path | ||||||
| from zope.authentication.interfaces import IUnauthenticatedPrincipal | import urllib | ||||||
| from zope.browserpage import ViewPageTemplateFile |  | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope import component | from zope import component | ||||||
| from zope.catalog.interfaces import ICatalog | from zope.app.catalog.interfaces import ICatalog | ||||||
| from zope.container.interfaces import INameChooser | from zope.app.container.interfaces import INameChooser | ||||||
|  | from zope.app.form.browser.textwidgets import FileWidget | ||||||
|  | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
|  | from zope.app.security.interfaces import IUnauthenticatedPrincipal | ||||||
| from zope.formlib.form import FormFields | from zope.formlib.form import FormFields | ||||||
| from zope.formlib.interfaces import DISPLAY_UNWRITEABLE | from zope.formlib.interfaces import DISPLAY_UNWRITEABLE | ||||||
| from zope.formlib.textwidgets import FileWidget |  | ||||||
| from zope.proxy import removeAllProxies | from zope.proxy import removeAllProxies | ||||||
| from zope.schema.interfaces import IBytes | from zope.schema.interfaces import IBytes | ||||||
| from zope.security import canAccess, canWrite | from zope.security import canAccess, canWrite | ||||||
|  | @ -254,7 +272,7 @@ class ResourceView(BaseView): | ||||||
|             #wp = wiki.createPage(getName(self.context)) |             #wp = wiki.createPage(getName(self.context)) | ||||||
|             wp = wiki.addPage(LoopsWikiPage(self.context)) |             wp = wiki.addPage(LoopsWikiPage(self.context)) | ||||||
|             wp.text = text |             wp.text = text | ||||||
|             #print(wp.wiki.getManager()) |             #print wp.wiki.getManager() | ||||||
|             #return util.toUnicode(wp.render(self.request)) |             #return util.toUnicode(wp.render(self.request)) | ||||||
|         return super(ResourceView, self).renderText(text, contentType) |         return super(ResourceView, self).renderText(text, contentType) | ||||||
| 
 | 
 | ||||||
|  | @ -449,7 +467,7 @@ class ExternalEditorView(ExternalEditorView, BaseView): | ||||||
|         r.append('meta_type:' + '.'.join((context.__module__, context.__class__.__name__))) |         r.append('meta_type:' + '.'.join((context.__module__, context.__class__.__name__))) | ||||||
|         auth = self.request.get('_auth') |         auth = self.request.get('_auth') | ||||||
|         if auth: |         if auth: | ||||||
|             print('ExternalEditorView: auth = ', auth) |             print 'ExternalEditorView: auth = ', auth | ||||||
|             if auth.endswith('\n'): |             if auth.endswith('\n'): | ||||||
|                 auth = auth[:-1] |                 auth = auth[:-1] | ||||||
|             r.append('auth:' + auth) |             r.append('auth:' + auth) | ||||||
| Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 580 B | 
| Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB | 
|  | @ -1,13 +1,29 @@ | ||||||
| # loops.browser.util | # | ||||||
|  | #  Copyright (c) 2011 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Utilities. | """ | ||||||
|  | Utilities. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import re | import re, urllib | ||||||
| import urllib.parse | from zope.app.pagetemplate import ViewPageTemplateFile | ||||||
| from zope.browserpage import ViewPageTemplateFile | from zope.app.publisher.browser.menu import BrowserMenu | ||||||
| from zope.browsermenu.menu import BrowserMenu | from zope.app.publisher.interfaces.browser import IBrowserSubMenuItem | ||||||
| from zope.browsermenu.interfaces import IBrowserSubMenuItem |  | ||||||
| from zope import component | from zope import component | ||||||
| from zope.formlib.namedtemplate import NamedTemplateImplementation | from zope.formlib.namedtemplate import NamedTemplateImplementation | ||||||
| 
 | 
 | ||||||
|  | @ -52,4 +68,4 @@ def html_quote(text, character_entities=((u'&', u'&'), (u'<', u'<' ), | ||||||
| pattern = re.compile(r'[ /\?\+\|%]') | pattern = re.compile(r'[ /\?\+\|%]') | ||||||
| 
 | 
 | ||||||
| def normalizeForUrl(text): | def normalizeForUrl(text): | ||||||
|     return urllib.parse.quote(pattern.sub('-', text).encode('UTF-8')) |     return urllib.quote(pattern.sub('-', text).encode('UTF-8')) | ||||||
|  | @ -4,6 +4,8 @@ loops - Linked Objects for Organization and Processing Services | ||||||
| 
 | 
 | ||||||
| Automatic classification of resources. | Automatic classification of resources. | ||||||
| 
 | 
 | ||||||
|  |   ($Id$) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| Setting up a loops Site and Utilities | Setting up a loops Site and Utilities | ||||||
| ===================================== | ===================================== | ||||||
|  | @ -37,7 +39,7 @@ from external files so we have something to work with. | ||||||
| 
 | 
 | ||||||
|   >>> tExternalCollection = concepts['extcollection'] |   >>> tExternalCollection = concepts['extcollection'] | ||||||
|   >>> coll01 = addObject(concepts, Concept, 'coll01', |   >>> coll01 = addObject(concepts, Concept, 'coll01', | ||||||
|   ...                    title='Collection One', conceptType=tExternalCollection) |   ...                    title=u'Collection One', conceptType=tExternalCollection) | ||||||
|   >>> aColl01 = adapted(coll01) |   >>> aColl01 = adapted(coll01) | ||||||
|   >>> aColl01.baseAddress = dataDir |   >>> aColl01.baseAddress = dataDir | ||||||
|   >>> aColl01.address = '' |   >>> aColl01.address = '' | ||||||
|  | @ -47,7 +49,7 @@ from external files so we have something to work with. | ||||||
|   7 |   7 | ||||||
|   >>> rnames = list(sorted(resources.keys())) |   >>> rnames = list(sorted(resources.keys())) | ||||||
|   >>> rnames[0] |   >>> rnames[0] | ||||||
|   'cust_im_contract_webbg_20071015.txt' |   u'cust_im_contract_webbg_20071015.txt' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Filename-based Classification | Filename-based Classification | ||||||
|  | @ -74,7 +76,7 @@ and follow the classifier step by step. | ||||||
|   >>> from loops.classifier.interfaces import IExtractor, IAnalyzer |   >>> from loops.classifier.interfaces import IExtractor, IAnalyzer | ||||||
|   >>> infoSet = InformationSet() |   >>> infoSet = InformationSet() | ||||||
|   >>> for name in classifier.extractors.split(): |   >>> for name in classifier.extractors.split(): | ||||||
|   ...     print('extractor:', name) |   ...     print 'extractor:', name | ||||||
|   ...     extractor = component.getAdapter(adapted(r1), IExtractor, name=name) |   ...     extractor = component.getAdapter(adapted(r1), IExtractor, name=name) | ||||||
|   ...     infoSet.update(extractor.extractInformationSet()) |   ...     infoSet.update(extractor.extractInformationSet()) | ||||||
|   extractor: filename |   extractor: filename | ||||||
|  | @ -94,32 +96,32 @@ So there seems to be something missing - we have to create concepts | ||||||
| that may be identified as being candidates for classification. | that may be identified as being candidates for classification. | ||||||
| 
 | 
 | ||||||
|   >>> tInstitution = addObject(concepts, Concept, 'institution', |   >>> tInstitution = addObject(concepts, Concept, 'institution', | ||||||
|   ...                     title='Institution', conceptType=concepts['type']) |   ...                     title=u'Institution', conceptType=concepts['type']) | ||||||
|   >>> cust_im = addObject(concepts, Concept, 'im_editors', |   >>> cust_im = addObject(concepts, Concept, 'im_editors', | ||||||
|   ...                     title='im Editors', conceptType=tInstitution) |   ...                     title=u'im Editors', conceptType=tInstitution) | ||||||
|   >>> cust_mc = addObject(concepts, Concept, 'mc_consulting', |   >>> cust_mc = addObject(concepts, Concept, 'mc_consulting', | ||||||
|   ...                     title='MC Management Consulting', conceptType=tInstitution) |   ...                     title=u'MC Management Consulting', conceptType=tInstitution) | ||||||
| 
 | 
 | ||||||
|   >>> tDoctype = addObject(concepts, Concept, 'doctype', |   >>> tDoctype = addObject(concepts, Concept, 'doctype', | ||||||
|   ...                     title='Document Type', conceptType=concepts['type']) |   ...                     title=u'Document Type', conceptType=concepts['type']) | ||||||
|   >>> dt_note = addObject(concepts, Concept, 'dt_note', |   >>> dt_note = addObject(concepts, Concept, 'dt_note', | ||||||
|   ...                     title='Note', conceptType=tDoctype) |   ...                     title=u'Note', conceptType=tDoctype) | ||||||
|   >>> dt_contract = addObject(concepts, Concept, 'dt_contract', |   >>> dt_contract = addObject(concepts, Concept, 'dt_contract', | ||||||
|   ...                     title='Contract', conceptType=tDoctype) |   ...                     title=u'Contract', conceptType=tDoctype) | ||||||
| 
 | 
 | ||||||
|   >>> tPerson = concepts['person'] |   >>> tPerson = concepts['person'] | ||||||
|   >>> webbg = addObject(concepts, Concept, 'webbg', |   >>> webbg = addObject(concepts, Concept, 'webbg', | ||||||
|   ...                     title='Gerald Webb', conceptType=tPerson) |   ...                     title=u'Gerald Webb', conceptType=tPerson) | ||||||
|   >>> smitha = addObject(concepts, Concept, 'smitha', |   >>> smitha = addObject(concepts, Concept, 'smitha', | ||||||
|   ...                     title='Angelina Smith', conceptType=tPerson) |   ...                     title=u'Angelina Smith', conceptType=tPerson) | ||||||
|   >>> watersj = addObject(concepts, Concept, 'watersj', |   >>> watersj = addObject(concepts, Concept, 'watersj', | ||||||
|   ...                     title='Jerry Waters', conceptType=tPerson) |   ...                     title=u'Jerry Waters', conceptType=tPerson) | ||||||
|   >>> millerj = addObject(concepts, Concept, 'millerj', |   >>> millerj = addObject(concepts, Concept, 'millerj', | ||||||
|   ...                     title='Jeannie Miller', conceptType=tPerson) |   ...                     title=u'Jeannie Miller', conceptType=tPerson) | ||||||
| 
 | 
 | ||||||
|   >>> t.indexAll(concepts, resources) |   >>> t.indexAll(concepts, resources) | ||||||
| 
 | 
 | ||||||
|   >>> from zope.catalog.interfaces import ICatalog |   >>> from zope.app.catalog.interfaces import ICatalog | ||||||
|   >>> cat = component.getUtility(ICatalog) |   >>> cat = component.getUtility(ICatalog) | ||||||
| 
 | 
 | ||||||
|   >>> statements = analyzer.extractStatements(infoSet) |   >>> statements = analyzer.extractStatements(infoSet) | ||||||
|  | @ -133,7 +135,7 @@ So we are now ready to have the whole stuff run in one call. | ||||||
| Classifier fileclassifier: Assigning: ... | Classifier fileclassifier: Assigning: ... | ||||||
| 
 | 
 | ||||||
|   >>> list(sorted([c.title for c in r1.getConcepts()])) |   >>> list(sorted([c.title for c in r1.getConcepts()])) | ||||||
|   ['Collection One', 'Contract', 'External File', 'Gerald Webb', 'im Editors'] |   [u'Collection One', u'Contract', u'External File', u'Gerald Webb', u'im Editors'] | ||||||
| 
 | 
 | ||||||
|   >>> for name in rnames[1:]: |   >>> for name in rnames[1:]: | ||||||
|   ...     classifier.process(resources[name]) |   ...     classifier.process(resources[name]) | ||||||
|  | @ -1,6 +1,23 @@ | ||||||
| # looops.classifier.base | # | ||||||
|  | #  Copyright (c) 2015 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Adapters and others classes for analyzing resources. | """ | ||||||
|  | Adapters and others classes for analyzing resources. | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from itertools import tee | from itertools import tee | ||||||
|  | @ -9,7 +26,7 @@ from zope.cachedescriptors.property import Lazy | ||||||
| from zope import component | from zope import component | ||||||
| from zope.component import adapts | from zope.component import adapts | ||||||
| from zope.event import notify | from zope.event import notify | ||||||
| from zope.interface import implementer | from zope.interface import implements | ||||||
| from zope.traversing.api import getName, getParent | from zope.traversing.api import getName, getParent | ||||||
| from cybertools.typology.interfaces import IType | from cybertools.typology.interfaces import IType | ||||||
| 
 | 
 | ||||||
|  | @ -27,11 +44,11 @@ logger = getLogger('Classifier') | ||||||
| TypeInterfaceSourceList.typeInterfaces += (IClassifier,) | TypeInterfaceSourceList.typeInterfaces += (IClassifier,) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(IClassifier) |  | ||||||
| class Classifier(AdapterBase): | class Classifier(AdapterBase): | ||||||
|     """ A concept adapter for analyzing resources. |     """ A concept adapter for analyzing resources. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  |     implements(IClassifier) | ||||||
|     adapts(IConcept) |     adapts(IConcept) | ||||||
| 
 | 
 | ||||||
|     _contextAttributes = list(IClassifier) + list(IConcept) |     _contextAttributes = list(IClassifier) + list(IConcept) | ||||||
|  | @ -95,9 +112,9 @@ class Classifier(AdapterBase): | ||||||
|             logger.info(u'%s: %s' % (getName(self.context), message)) |             logger.info(u'%s: %s' % (getName(self.context), message)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(IExtractor) |  | ||||||
| class Extractor(object): | class Extractor(object): | ||||||
| 
 | 
 | ||||||
|  |     implements(IExtractor) | ||||||
|     adapts(IResource) |     adapts(IResource) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, context): |     def __init__(self, context): | ||||||
|  | @ -107,9 +124,9 @@ class Extractor(object): | ||||||
|         return InformationSet() |         return InformationSet() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(IAnalyzer) |  | ||||||
| class Analyzer(object): | class Analyzer(object): | ||||||
| 
 | 
 | ||||||
|  |     implements(IAnalyzer) | ||||||
|     adapts(IClassifier) |     adapts(IClassifier) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, context): |     def __init__(self, context): | ||||||
|  | @ -130,15 +147,15 @@ class Analyzer(object): | ||||||
|         return r1 |         return r1 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(IInformationSet) |  | ||||||
| class InformationSet(dict): | class InformationSet(dict): | ||||||
| 
 | 
 | ||||||
|     pass |     implements(IInformationSet) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @implementer(IStatement) |  | ||||||
| class Statement(object): | class Statement(object): | ||||||
| 
 | 
 | ||||||
|  |     implements(IStatement) | ||||||
|  | 
 | ||||||
|     def __init__(self, object=None, predicate=None, subject=None, relevance=100): |     def __init__(self, object=None, predicate=None, subject=None, relevance=100): | ||||||
|         self.subject = subject |         self.subject = subject | ||||||
|         self.predicate = predicate |         self.predicate = predicate | ||||||
|  | @ -1,11 +1,30 @@ | ||||||
| # loops.classifier.sample | # | ||||||
|  | #  Copyright (c) 2007 Helmut Merz helmutm@cy55.de | ||||||
|  | # | ||||||
|  | #  This program is free software; you can redistribute it and/or modify | ||||||
|  | #  it under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation; either version 2 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  This program is distributed in the hope that it will be useful, | ||||||
|  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | #  GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with this program; if not, write to the Free Software | ||||||
|  | #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||||
|  | # | ||||||
| 
 | 
 | ||||||
| """ Sample classifier implementation. | """ | ||||||
|  | Sample classifier implementation. | ||||||
|  | 
 | ||||||
|  | $Id$ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| from zope import component | from zope import component | ||||||
|  | from zope.app.catalog.interfaces import ICatalog | ||||||
| from zope.cachedescriptors.property import Lazy | from zope.cachedescriptors.property import Lazy | ||||||
| from zope.catalog.interfaces import ICatalog |  | ||||||
| from zope.component import adapts | from zope.component import adapts | ||||||
| from zope.traversing.api import getName | from zope.traversing.api import getName | ||||||
| 
 | 
 | ||||||