Commit a1e9df306f7b6a443517379c00efeb0d033713c0
0 parents
Exists in
master
initial release
Showing
38 changed files
with
2199 additions
and
0 deletions
Show diff stats
1 | +++ a/docs/HISTORY.txt | |
... | ... | @@ -0,0 +1,51 @@ |
1 | +Changelog | |
2 | +========= | |
3 | + | |
4 | +1.1.2 - 2015-06-05 | |
5 | +--------------------- | |
6 | + | |
7 | +- Inclusão de novas views para exibição de pessoas por unidade: unidade, unidade_iframe (para uso na Intranet) e unidade_json. | |
8 | + [bruno.bbbs, jannie.jecb] | |
9 | + | |
10 | + | |
11 | +1.1.1 - 2015-05-27 | |
12 | +--------------------- | |
13 | + | |
14 | +- Melhoria na distribuição de arquivos javascript entre os pacotes de domínio pf.* (Observado pelo desenvolvimento) | |
15 | + [bruno.bbbs] | |
16 | + | |
17 | + | |
18 | +1.1.0 - 2015-05-15 | |
19 | +--------------------- | |
20 | + | |
21 | +- Inclusão de nova funcionalidade de busca de pessoas por unidade via URL. (OTRS #10163898) | |
22 | + [bruno.bbbs] | |
23 | +- Correcao da responsividade do formulario e remocao do css duplicado. (OTRS #10167466) | |
24 | + [jannie.jecb] | |
25 | + | |
26 | + | |
27 | +1.0.6 - 2015-05-08 | |
28 | +--------------------- | |
29 | + | |
30 | +- Correção e melhorias na pesquisa da Lista Telefônica (OTRS #10165913) | |
31 | + [bruno.bbbs] | |
32 | + | |
33 | + | |
34 | +1.0.5 - 2015-04-28 | |
35 | +--------------------- | |
36 | + | |
37 | +- Retirada a obrigatoriedade do campo "função". (Observado pelo desenvolvimento) | |
38 | + [bruno.bbbs] | |
39 | + | |
40 | + | |
41 | +1.0.4 - 2015-04-24 | |
42 | +--------------------- | |
43 | + | |
44 | +- Correção no campo "cargo" na visualização dos dados da pessoa que exibia a unidade. (Observado pelo desenvolvimento) | |
45 | + [bruno.bbbs] | |
46 | + | |
47 | + | |
48 | +1.0.3 - Initial release | |
49 | +--------------------- | |
50 | + | |
51 | +- Initial release | ... | ... |
1 | +++ a/docs/INSTALL.txt | |
... | ... | @@ -0,0 +1,52 @@ |
1 | +pf.listatelefonica Installation | |
2 | +------------------------------- | |
3 | + | |
4 | +To install pf.listatelefonica into the global Python environment (or a workingenv), | |
5 | +using a traditional Zope 2 instance, you can do this: | |
6 | + | |
7 | +* When you're reading this you have probably already run | |
8 | + ``easy_install pf.listatelefonica``. Find out how to install setuptools | |
9 | + (and EasyInstall) here: | |
10 | + http://peak.telecommunity.com/DevCenter/EasyInstall | |
11 | + | |
12 | +* If you are using Zope 2.9 (not 2.10), get `pythonproducts`_ and install it | |
13 | + via:: | |
14 | + | |
15 | + python setup.py install --home /path/to/instance | |
16 | + | |
17 | +into your Zope instance. | |
18 | + | |
19 | +* Create a file called ``pf.listatelefonica-configure.zcml`` in the | |
20 | + ``/path/to/instance/etc/package-includes`` directory. The file | |
21 | + should only contain this:: | |
22 | + | |
23 | + <include package="pf.listatelefonica" /> | |
24 | + | |
25 | +.. _pythonproducts: http://plone.org/products/pythonproducts | |
26 | + | |
27 | + | |
28 | +Alternatively, if you are using zc.buildout and the plone.recipe.zope2instance | |
29 | +recipe to manage your project, you can do this: | |
30 | + | |
31 | +* Add ``pf.listatelefonica`` to the list of eggs to install, e.g.: | |
32 | + | |
33 | + [buildout] | |
34 | + ... | |
35 | + eggs = | |
36 | + ... | |
37 | + pf.listatelefonica | |
38 | + | |
39 | +* Tell the plone.recipe.zope2instance recipe to install a ZCML slug: | |
40 | + | |
41 | + [instance] | |
42 | + recipe = plone.recipe.zope2instance | |
43 | + ... | |
44 | + zcml = | |
45 | + pf.listatelefonica | |
46 | + | |
47 | +* Re-run buildout, e.g. with: | |
48 | + | |
49 | + $ ./bin/buildout | |
50 | + | |
51 | +You can skip the ZCML slug if you are going to explicitly include the package | |
52 | +from another package's configure.zcml file. | ... | ... |
1 | +++ a/docs/LICENSE.GPL | |
... | ... | @@ -0,0 +1,339 @@ |
1 | + GNU GENERAL PUBLIC LICENSE | |
2 | + Version 2, June 1991 | |
3 | + | |
4 | + Copyright (C) 1989, 1991 Free Software Foundation, Inc., | |
5 | + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
6 | + Everyone is permitted to copy and distribute verbatim copies | |
7 | + of this license document, but changing it is not allowed. | |
8 | + | |
9 | + Preamble | |
10 | + | |
11 | + The licenses for most software are designed to take away your | |
12 | +freedom to share and change it. By contrast, the GNU General Public | |
13 | +License is intended to guarantee your freedom to share and change free | |
14 | +software--to make sure the software is free for all its users. This | |
15 | +General Public License applies to most of the Free Software | |
16 | +Foundation's software and to any other program whose authors commit to | |
17 | +using it. (Some other Free Software Foundation software is covered by | |
18 | +the GNU Lesser General Public License instead.) You can apply it to | |
19 | +your programs, too. | |
20 | + | |
21 | + When we speak of free software, we are referring to freedom, not | |
22 | +price. Our General Public Licenses are designed to make sure that you | |
23 | +have the freedom to distribute copies of free software (and charge for | |
24 | +this service if you wish), that you receive source code or can get it | |
25 | +if you want it, that you can change the software or use pieces of it | |
26 | +in new free programs; and that you know you can do these things. | |
27 | + | |
28 | + To protect your rights, we need to make restrictions that forbid | |
29 | +anyone to deny you these rights or to ask you to surrender the rights. | |
30 | +These restrictions translate to certain responsibilities for you if you | |
31 | +distribute copies of the software, or if you modify it. | |
32 | + | |
33 | + For example, if you distribute copies of such a program, whether | |
34 | +gratis or for a fee, you must give the recipients all the rights that | |
35 | +you have. You must make sure that they, too, receive or can get the | |
36 | +source code. And you must show them these terms so they know their | |
37 | +rights. | |
38 | + | |
39 | + We protect your rights with two steps: (1) copyright the software, and | |
40 | +(2) offer you this license which gives you legal permission to copy, | |
41 | +distribute and/or modify the software. | |
42 | + | |
43 | + Also, for each author's protection and ours, we want to make certain | |
44 | +that everyone understands that there is no warranty for this free | |
45 | +software. If the software is modified by someone else and passed on, we | |
46 | +want its recipients to know that what they have is not the original, so | |
47 | +that any problems introduced by others will not reflect on the original | |
48 | +authors' reputations. | |
49 | + | |
50 | + Finally, any free program is threatened constantly by software | |
51 | +patents. We wish to avoid the danger that redistributors of a free | |
52 | +program will individually obtain patent licenses, in effect making the | |
53 | +program proprietary. To prevent this, we have made it clear that any | |
54 | +patent must be licensed for everyone's free use or not licensed at all. | |
55 | + | |
56 | + The precise terms and conditions for copying, distribution and | |
57 | +modification follow. | |
58 | + | |
59 | + GNU GENERAL PUBLIC LICENSE | |
60 | + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
61 | + | |
62 | + 0. This License applies to any program or other work which contains | |
63 | +a notice placed by the copyright holder saying it may be distributed | |
64 | +under the terms of this General Public License. The "Program", below, | |
65 | +refers to any such program or work, and a "work based on the Program" | |
66 | +means either the Program or any derivative work under copyright law: | |
67 | +that is to say, a work containing the Program or a portion of it, | |
68 | +either verbatim or with modifications and/or translated into another | |
69 | +language. (Hereinafter, translation is included without limitation in | |
70 | +the term "modification".) Each licensee is addressed as "you". | |
71 | + | |
72 | +Activities other than copying, distribution and modification are not | |
73 | +covered by this License; they are outside its scope. The act of | |
74 | +running the Program is not restricted, and the output from the Program | |
75 | +is covered only if its contents constitute a work based on the | |
76 | +Program (independent of having been made by running the Program). | |
77 | +Whether that is true depends on what the Program does. | |
78 | + | |
79 | + 1. You may copy and distribute verbatim copies of the Program's | |
80 | +source code as you receive it, in any medium, provided that you | |
81 | +conspicuously and appropriately publish on each copy an appropriate | |
82 | +copyright notice and disclaimer of warranty; keep intact all the | |
83 | +notices that refer to this License and to the absence of any warranty; | |
84 | +and give any other recipients of the Program a copy of this License | |
85 | +along with the Program. | |
86 | + | |
87 | +You may charge a fee for the physical act of transferring a copy, and | |
88 | +you may at your option offer warranty protection in exchange for a fee. | |
89 | + | |
90 | + 2. You may modify your copy or copies of the Program or any portion | |
91 | +of it, thus forming a work based on the Program, and copy and | |
92 | +distribute such modifications or work under the terms of Section 1 | |
93 | +above, provided that you also meet all of these conditions: | |
94 | + | |
95 | + a) You must cause the modified files to carry prominent notices | |
96 | + stating that you changed the files and the date of any change. | |
97 | + | |
98 | + b) You must cause any work that you distribute or publish, that in | |
99 | + whole or in part contains or is derived from the Program or any | |
100 | + part thereof, to be licensed as a whole at no charge to all third | |
101 | + parties under the terms of this License. | |
102 | + | |
103 | + c) If the modified program normally reads commands interactively | |
104 | + when run, you must cause it, when started running for such | |
105 | + interactive use in the most ordinary way, to print or display an | |
106 | + announcement including an appropriate copyright notice and a | |
107 | + notice that there is no warranty (or else, saying that you provide | |
108 | + a warranty) and that users may redistribute the program under | |
109 | + these conditions, and telling the user how to view a copy of this | |
110 | + License. (Exception: if the Program itself is interactive but | |
111 | + does not normally print such an announcement, your work based on | |
112 | + the Program is not required to print an announcement.) | |
113 | + | |
114 | +These requirements apply to the modified work as a whole. If | |
115 | +identifiable sections of that work are not derived from the Program, | |
116 | +and can be reasonably considered independent and separate works in | |
117 | +themselves, then this License, and its terms, do not apply to those | |
118 | +sections when you distribute them as separate works. But when you | |
119 | +distribute the same sections as part of a whole which is a work based | |
120 | +on the Program, the distribution of the whole must be on the terms of | |
121 | +this License, whose permissions for other licensees extend to the | |
122 | +entire whole, and thus to each and every part regardless of who wrote it. | |
123 | + | |
124 | +Thus, it is not the intent of this section to claim rights or contest | |
125 | +your rights to work written entirely by you; rather, the intent is to | |
126 | +exercise the right to control the distribution of derivative or | |
127 | +collective works based on the Program. | |
128 | + | |
129 | +In addition, mere aggregation of another work not based on the Program | |
130 | +with the Program (or with a work based on the Program) on a volume of | |
131 | +a storage or distribution medium does not bring the other work under | |
132 | +the scope of this License. | |
133 | + | |
134 | + 3. You may copy and distribute the Program (or a work based on it, | |
135 | +under Section 2) in object code or executable form under the terms of | |
136 | +Sections 1 and 2 above provided that you also do one of the following: | |
137 | + | |
138 | + a) Accompany it with the complete corresponding machine-readable | |
139 | + source code, which must be distributed under the terms of Sections | |
140 | + 1 and 2 above on a medium customarily used for software interchange; or, | |
141 | + | |
142 | + b) Accompany it with a written offer, valid for at least three | |
143 | + years, to give any third party, for a charge no more than your | |
144 | + cost of physically performing source distribution, a complete | |
145 | + machine-readable copy of the corresponding source code, to be | |
146 | + distributed under the terms of Sections 1 and 2 above on a medium | |
147 | + customarily used for software interchange; or, | |
148 | + | |
149 | + c) Accompany it with the information you received as to the offer | |
150 | + to distribute corresponding source code. (This alternative is | |
151 | + allowed only for noncommercial distribution and only if you | |
152 | + received the program in object code or executable form with such | |
153 | + an offer, in accord with Subsection b above.) | |
154 | + | |
155 | +The source code for a work means the preferred form of the work for | |
156 | +making modifications to it. For an executable work, complete source | |
157 | +code means all the source code for all modules it contains, plus any | |
158 | +associated interface definition files, plus the scripts used to | |
159 | +control compilation and installation of the executable. However, as a | |
160 | +special exception, the source code distributed need not include | |
161 | +anything that is normally distributed (in either source or binary | |
162 | +form) with the major components (compiler, kernel, and so on) of the | |
163 | +operating system on which the executable runs, unless that component | |
164 | +itself accompanies the executable. | |
165 | + | |
166 | +If distribution of executable or object code is made by offering | |
167 | +access to copy from a designated place, then offering equivalent | |
168 | +access to copy the source code from the same place counts as | |
169 | +distribution of the source code, even though third parties are not | |
170 | +compelled to copy the source along with the object code. | |
171 | + | |
172 | + 4. You may not copy, modify, sublicense, or distribute the Program | |
173 | +except as expressly provided under this License. Any attempt | |
174 | +otherwise to copy, modify, sublicense or distribute the Program is | |
175 | +void, and will automatically terminate your rights under this License. | |
176 | +However, parties who have received copies, or rights, from you under | |
177 | +this License will not have their licenses terminated so long as such | |
178 | +parties remain in full compliance. | |
179 | + | |
180 | + 5. You are not required to accept this License, since you have not | |
181 | +signed it. However, nothing else grants you permission to modify or | |
182 | +distribute the Program or its derivative works. These actions are | |
183 | +prohibited by law if you do not accept this License. Therefore, by | |
184 | +modifying or distributing the Program (or any work based on the | |
185 | +Program), you indicate your acceptance of this License to do so, and | |
186 | +all its terms and conditions for copying, distributing or modifying | |
187 | +the Program or works based on it. | |
188 | + | |
189 | + 6. Each time you redistribute the Program (or any work based on the | |
190 | +Program), the recipient automatically receives a license from the | |
191 | +original licensor to copy, distribute or modify the Program subject to | |
192 | +these terms and conditions. You may not impose any further | |
193 | +restrictions on the recipients' exercise of the rights granted herein. | |
194 | +You are not responsible for enforcing compliance by third parties to | |
195 | +this License. | |
196 | + | |
197 | + 7. If, as a consequence of a court judgment or allegation of patent | |
198 | +infringement or for any other reason (not limited to patent issues), | |
199 | +conditions are imposed on you (whether by court order, agreement or | |
200 | +otherwise) that contradict the conditions of this License, they do not | |
201 | +excuse you from the conditions of this License. If you cannot | |
202 | +distribute so as to satisfy simultaneously your obligations under this | |
203 | +License and any other pertinent obligations, then as a consequence you | |
204 | +may not distribute the Program at all. For example, if a patent | |
205 | +license would not permit royalty-free redistribution of the Program by | |
206 | +all those who receive copies directly or indirectly through you, then | |
207 | +the only way you could satisfy both it and this License would be to | |
208 | +refrain entirely from distribution of the Program. | |
209 | + | |
210 | +If any portion of this section is held invalid or unenforceable under | |
211 | +any particular circumstance, the balance of the section is intended to | |
212 | +apply and the section as a whole is intended to apply in other | |
213 | +circumstances. | |
214 | + | |
215 | +It is not the purpose of this section to induce you to infringe any | |
216 | +patents or other property right claims or to contest validity of any | |
217 | +such claims; this section has the sole purpose of protecting the | |
218 | +integrity of the free software distribution system, which is | |
219 | +implemented by public license practices. Many people have made | |
220 | +generous contributions to the wide range of software distributed | |
221 | +through that system in reliance on consistent application of that | |
222 | +system; it is up to the author/donor to decide if he or she is willing | |
223 | +to distribute software through any other system and a licensee cannot | |
224 | +impose that choice. | |
225 | + | |
226 | +This section is intended to make thoroughly clear what is believed to | |
227 | +be a consequence of the rest of this License. | |
228 | + | |
229 | + 8. If the distribution and/or use of the Program is restricted in | |
230 | +certain countries either by patents or by copyrighted interfaces, the | |
231 | +original copyright holder who places the Program under this License | |
232 | +may add an explicit geographical distribution limitation excluding | |
233 | +those countries, so that distribution is permitted only in or among | |
234 | +countries not thus excluded. In such case, this License incorporates | |
235 | +the limitation as if written in the body of this License. | |
236 | + | |
237 | + 9. The Free Software Foundation may publish revised and/or new versions | |
238 | +of the General Public License from time to time. Such new versions will | |
239 | +be similar in spirit to the present version, but may differ in detail to | |
240 | +address new problems or concerns. | |
241 | + | |
242 | +Each version is given a distinguishing version number. If the Program | |
243 | +specifies a version number of this License which applies to it and "any | |
244 | +later version", you have the option of following the terms and conditions | |
245 | +either of that version or of any later version published by the Free | |
246 | +Software Foundation. If the Program does not specify a version number of | |
247 | +this License, you may choose any version ever published by the Free Software | |
248 | +Foundation. | |
249 | + | |
250 | + 10. If you wish to incorporate parts of the Program into other free | |
251 | +programs whose distribution conditions are different, write to the author | |
252 | +to ask for permission. For software which is copyrighted by the Free | |
253 | +Software Foundation, write to the Free Software Foundation; we sometimes | |
254 | +make exceptions for this. Our decision will be guided by the two goals | |
255 | +of preserving the free status of all derivatives of our free software and | |
256 | +of promoting the sharing and reuse of software generally. | |
257 | + | |
258 | + NO WARRANTY | |
259 | + | |
260 | + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | |
261 | +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | |
262 | +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | |
263 | +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | |
264 | +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
265 | +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | |
266 | +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | |
267 | +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | |
268 | +REPAIR OR CORRECTION. | |
269 | + | |
270 | + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |
271 | +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | |
272 | +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | |
273 | +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | |
274 | +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | |
275 | +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | |
276 | +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | |
277 | +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | |
278 | +POSSIBILITY OF SUCH DAMAGES. | |
279 | + | |
280 | + END OF TERMS AND CONDITIONS | |
281 | + | |
282 | + How to Apply These Terms to Your New Programs | |
283 | + | |
284 | + If you develop a new program, and you want it to be of the greatest | |
285 | +possible use to the public, the best way to achieve this is to make it | |
286 | +free software which everyone can redistribute and change under these terms. | |
287 | + | |
288 | + To do so, attach the following notices to the program. It is safest | |
289 | +to attach them to the start of each source file to most effectively | |
290 | +convey the exclusion of warranty; and each file should have at least | |
291 | +the "copyright" line and a pointer to where the full notice is found. | |
292 | + | |
293 | + <one line to give the program's name and a brief idea of what it does.> | |
294 | + Copyright (C) <year> <name of author> | |
295 | + | |
296 | + This program is free software; you can redistribute it and/or modify | |
297 | + it under the terms of the GNU General Public License as published by | |
298 | + the Free Software Foundation; either version 2 of the License, or | |
299 | + (at your option) any later version. | |
300 | + | |
301 | + This program is distributed in the hope that it will be useful, | |
302 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
303 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
304 | + GNU General Public License for more details. | |
305 | + | |
306 | + You should have received a copy of the GNU General Public License along | |
307 | + with this program; if not, write to the Free Software Foundation, Inc., | |
308 | + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
309 | + | |
310 | +Also add information on how to contact you by electronic and paper mail. | |
311 | + | |
312 | +If the program is interactive, make it output a short notice like this | |
313 | +when it starts in an interactive mode: | |
314 | + | |
315 | + Gnomovision version 69, Copyright (C) year name of author | |
316 | + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | |
317 | + This is free software, and you are welcome to redistribute it | |
318 | + under certain conditions; type `show c' for details. | |
319 | + | |
320 | +The hypothetical commands `show w' and `show c' should show the appropriate | |
321 | +parts of the General Public License. Of course, the commands you use may | |
322 | +be called something other than `show w' and `show c'; they could even be | |
323 | +mouse-clicks or menu items--whatever suits your program. | |
324 | + | |
325 | +You should also get your employer (if you work as a programmer) or your | |
326 | +school, if any, to sign a "copyright disclaimer" for the program, if | |
327 | +necessary. Here is a sample; alter the names: | |
328 | + | |
329 | + Yoyodyne, Inc., hereby disclaims all copyright interest in the program | |
330 | + `Gnomovision' (which makes passes at compilers) written by James Hacker. | |
331 | + | |
332 | + <signature of Ty Coon>, 1 April 1989 | |
333 | + Ty Coon, President of Vice | |
334 | + | |
335 | +This General Public License does not permit incorporating your program into | |
336 | +proprietary programs. If your program is a subroutine library, you may | |
337 | +consider it more useful to permit linking proprietary applications with the | |
338 | +library. If this is what you want to do, use the GNU Lesser General | |
339 | +Public License instead of this License. | ... | ... |
1 | +++ a/docs/LICENSE.txt | |
... | ... | @@ -0,0 +1,16 @@ |
1 | + pf.listatelefonica is copyright | |
2 | + | |
3 | + This program is free software; you can redistribute it and/or modify | |
4 | + it under the terms of the GNU General Public License as published by | |
5 | + the Free Software Foundation; either version 2 of the License, or | |
6 | + (at your option) any later version. | |
7 | + | |
8 | + This program is distributed in the hope that it will be useful, | |
9 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | + GNU General Public License for more details. | |
12 | + | |
13 | + You should have received a copy of the GNU General Public License | |
14 | + along with this program; if not, write to the Free Software | |
15 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
16 | + MA 02111-1307 USA. | ... | ... |
1 | +++ a/pf/__init__.py | |
... | ... | @@ -0,0 +1,6 @@ |
1 | +# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages | |
2 | +try: | |
3 | + __import__('pkg_resources').declare_namespace(__name__) | |
4 | +except ImportError: | |
5 | + from pkgutil import extend_path | |
6 | + __path__ = extend_path(__path__, __name__) | ... | ... |
1 | +++ a/pf/listatelefonica/Extensions/install.py | |
... | ... | @@ -0,0 +1,19 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | + | |
3 | +from plone import api | |
4 | + | |
5 | +from pf.listatelefonica import logger | |
6 | + | |
7 | + | |
8 | +def uninstall(portal, reinstall=False): | |
9 | + if not reinstall: | |
10 | + content = api.content.get(path='/lista-telefonica') | |
11 | + content_link = api.content.get(path='/servicos-do-portal/lista-telefonica') | |
12 | + | |
13 | + if content: | |
14 | + content.setLayout('folder_listing') | |
15 | + | |
16 | + if content_link: | |
17 | + api.content.delete(obj=content_link) | |
18 | + | |
19 | + logger.info("Desinstalação do produto pf.listatelefonica completa") | ... | ... |
1 | +++ a/pf/listatelefonica/__init__.py | |
... | ... | @@ -0,0 +1,7 @@ |
1 | +from zope.i18nmessageid import MessageFactory | |
2 | + | |
3 | +import logging | |
4 | + | |
5 | +# Set up the i18n message factory for our package | |
6 | +MessageFactory = MessageFactory('pf.listatelefonica') | |
7 | +logger = logging.getLogger('pf.biblioteca') | ... | ... |
1 | +++ a/pf/listatelefonica/browser/__init__.py | ... | ... |
1 | +++ a/pf/listatelefonica/browser/configure.zcml | |
... | ... | @@ -0,0 +1,8 @@ |
1 | +<configure | |
2 | + xmlns:browser="http://namespaces.zope.org/browser" | |
3 | + xmlns="http://namespaces.zope.org/browser" | |
4 | + xmlns:plone="http://namespaces.plone.org/plone" | |
5 | + i18n_domain="pf.listatelefonica"> | |
6 | + | |
7 | + | |
8 | +</configure> | ... | ... |
pf/listatelefonica/browser/templates/listatelefonicaview.pt
0 → 100644
1 | +++ a/pf/listatelefonica/browser/templates/listatelefonicaview.pt | |
... | ... | @@ -0,0 +1,45 @@ |
1 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" | |
2 | + xmlns:tal="http://xml.zope.org/namespaces/tal" | |
3 | + xmlns:metal="http://xml.zope.org/namespaces/metal" | |
4 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" | |
5 | + lang="pt-br" | |
6 | + metal:use-macro="context/main_template/macros/master" | |
7 | + i18n:domain="pf.listatelefonica"> | |
8 | + | |
9 | +<metal:override fill-slot="top_slot" | |
10 | + tal:define="dummy python:request.set('disable_border', 1)" /> | |
11 | + | |
12 | + | |
13 | +<body> | |
14 | + | |
15 | + <metal:main fill-slot="main"> | |
16 | + | |
17 | + <!-- Login / Cadastrar / Editar --> | |
18 | + <metal:block use-macro="context/listacadastro_view/macros/cadastro" /> | |
19 | + | |
20 | + <div class="row"> | |
21 | + <div class="cell width-full position-0 "> | |
22 | + <div class="tile azul ui-droppable" > | |
23 | + <div class="outstanding-header"> | |
24 | + <h2 class="outstanding-title" tal:content="context/Title">Bem vindo ao Portal de Serviços da Intranet</h2> | |
25 | + </div> | |
26 | + </div> | |
27 | + </div> | |
28 | + </div> | |
29 | + <div class="row"> | |
30 | + <div class="cell width-full position-0 "> | |
31 | + <div tal:content="structure view/get_description|nothing"></div> | |
32 | + </div> | |
33 | + </div> | |
34 | + <div class="row"> | |
35 | + <div class="cell width-full position-0 "> | |
36 | + | |
37 | + <metal:block metal:use-macro="context/listatelefonica_busca_view/macros/busca" /> | |
38 | + | |
39 | + </div> | |
40 | + </div> | |
41 | +</metal:main> | |
42 | + | |
43 | +</body> | |
44 | + | |
45 | +</html> | ... | ... |
1 | +++ a/pf/listatelefonica/browser/templates/pessoaview.pt | |
... | ... | @@ -0,0 +1,69 @@ |
1 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" | |
2 | + xmlns:tal="http://xml.zope.org/namespaces/tal" | |
3 | + xmlns:metal="http://xml.zope.org/namespaces/metal" | |
4 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" | |
5 | + lang="pt-br" | |
6 | + metal:use-macro="context/main_template/macros/master" | |
7 | + i18n:domain="pf.listatelefonica"> | |
8 | + | |
9 | +<metal:override fill-slot="top_slot" | |
10 | + tal:define="dummy python:request.set('disable_border', 1)" /> | |
11 | + | |
12 | +<body> | |
13 | + | |
14 | + <metal:main fill-slot="main"> | |
15 | + | |
16 | + <div class="row"> | |
17 | + <div class="cell width-full position-0 "> | |
18 | + <div class="tile azul ui-droppable"> | |
19 | + <div class="outstanding-header"> | |
20 | + <h3 class="outstanding-title" | |
21 | + tal:content="context/unidade">Unidade</h3> | |
22 | + </div> | |
23 | + </div> | |
24 | + </div> | |
25 | + </div> | |
26 | + | |
27 | + <div class="row"> | |
28 | + <div class="cell width-full position-0 "> | |
29 | + <table class="listing"> | |
30 | + <tbody> | |
31 | + <tr class="odd"> | |
32 | + <th>Nome</th> | |
33 | + <th>Cargo</th> | |
34 | + <th>Função</th> | |
35 | + <th>Email</th> | |
36 | + <th>Ramal</th> | |
37 | + <th>Celular</th> | |
38 | + <th>Voip</th> | |
39 | + </tr> | |
40 | + <tr class="even"> | |
41 | + <td tal:content="context/nome">Nome</td> | |
42 | + <td tal:content="context/cargo">Cargo</td> | |
43 | + <td tal:content="context/funcao">Função</td> | |
44 | + <td tal:content="string:${context/title}@dpf.gov.br">Email</td> | |
45 | + <td tal:content="structure python:view.get_ramais(context)">Ramal</td> | |
46 | + <td tal:content="context/celular|nothing">Celular</td> | |
47 | + <td tal:content="context/voip|nothing">Voip</td> | |
48 | + </tr> | |
49 | + </tbody> | |
50 | + </table> | |
51 | + </div> | |
52 | + </div> | |
53 | + | |
54 | + <div class="row" | |
55 | + tal:condition="view/is_owner"> | |
56 | + <div class="cell width-full position-0 "> | |
57 | + <div class="tile azul ui-droppable" > | |
58 | + <div class="outstanding-header"> | |
59 | + <a href="" class="outstanding-link" | |
60 | + tal:attributes="href string:${context/absolute_url}/edit">Editar dados</a> | |
61 | + </div> | |
62 | + </div> | |
63 | + </div> | |
64 | + </div> | |
65 | + </metal:main> | |
66 | + | |
67 | +</body> | |
68 | + | |
69 | +</html> | ... | ... |
pf/listatelefonica/browser/templates/unidadesiframeview.pt
0 → 100755
1 | +++ a/pf/listatelefonica/browser/templates/unidadesiframeview.pt | |
... | ... | @@ -0,0 +1,95 @@ |
1 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" | |
2 | + xmlns:tal="http://xml.zope.org/namespaces/tal" | |
3 | + xmlns:metal="http://xml.zope.org/namespaces/metal" | |
4 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" | |
5 | + lang="pt-br" | |
6 | + i18n:domain="pf.listatelefonica"> | |
7 | + | |
8 | +<head> | |
9 | + <link rel="stylesheet" type="text/css" href="form_busca.css" media="screen" | |
10 | + tal:attributes="href string:${context/portal_url}/++resource++brasil.gov.tiles/tiles.css"> | |
11 | + <link rel="stylesheet" type="text/css" href="form_busca.css" media="screen" | |
12 | + tal:attributes="href string:${context/portal_url}/++resource++brasil.gov.portal/css/main.css"> | |
13 | + <link rel="stylesheet" type="text/css" href="form_busca.css" media="screen" | |
14 | + tal:attributes="href string:${context/portal_url}/++resource++pf.listatelefonica/unidades.css"> | |
15 | + </head> | |
16 | + | |
17 | +<body id="unidade-iframe"> | |
18 | + | |
19 | +<tal:items define="unidades view/persons"> | |
20 | + <tal:repeat condition="unidades" | |
21 | + repeat="unidade python:unidades[0]"> | |
22 | + <div class="row"> | |
23 | + <div class="cell width-full position-0 "> | |
24 | + <div class="tile azul ui-droppable"> | |
25 | + <div class="outstanding-header"> | |
26 | + <h3 class="outstanding-title" | |
27 | + tal:content="unidade|nothing">UNIDADE</h3> | |
28 | + </div> | |
29 | + </div> | |
30 | + </div> | |
31 | + </div> | |
32 | + <div class="row lista-person"> | |
33 | + <div class="cell width-full position-0 "> | |
34 | + <table class="listing" | |
35 | + tal:define="persons python:unidades[1]"> | |
36 | + <tbody> | |
37 | + <tr class="odd"> | |
38 | + <th>Nome</th> | |
39 | + <th>Cargo</th> | |
40 | + <th>Função</th> | |
41 | + <th>Ramal</th> | |
42 | + <th>Celular</th> | |
43 | + <th>Voip</th> | |
44 | + <th>Email</th> | |
45 | + </tr> | |
46 | + <tr class="even" | |
47 | + tal:condition="python: unidade in persons" | |
48 | + tal:repeat="person python:persons[unidade]"> | |
49 | + <td> | |
50 | + <a href="" target="_blank" title="" | |
51 | + tal:content="person/nome" | |
52 | + tal:attributes="href person/url; | |
53 | + title person/nome"> | |
54 | + Nome | |
55 | + </a> | |
56 | + </td> | |
57 | + <td tal:content="person/cargo">Cargo</td> | |
58 | + <td tal:content="person/funcao">Função</td> | |
59 | + <td tal:content="structure person/ramal">Ramal</td> | |
60 | + <td tal:content="person/celular|nothing">Celular</td> | |
61 | + <td tal:content="person/voip|nothing">Voip</td> | |
62 | + <td tal:content="person/email">Email</td> | |
63 | + </tr> | |
64 | + <tr class="odd" | |
65 | + tal:condition="not: persons"> | |
66 | + <td colspan="7"><span class="discreet">Nenhum registro encontrado</span></td> | |
67 | + </tr> | |
68 | + </tbody> | |
69 | + </table> | |
70 | + </div> | |
71 | + </div> | |
72 | + </tal:repeat> | |
73 | + <tal:notunity condition="not: unidades"> | |
74 | + <div class="row"> | |
75 | + <div class="cell width-full position-0 "> | |
76 | + <div class="tile azul ui-droppable"> | |
77 | + <div class="outstanding-header"> | |
78 | + <h3 class="outstanding-title">Lista Telefônica</h3> | |
79 | + </div> | |
80 | + </div> | |
81 | + </div> | |
82 | + </div> | |
83 | + <div class="row"> | |
84 | + <div class="cell width-full position-0 "> | |
85 | + <p>A Lista Telefônica do Portal de Serviços da Intranet não possui nomes cadastrados na unidade organizacional <span tal:content="request/q|nothing">DPF/CGTI</span>.</p> | |
86 | + <p>É possível que o nome da Unidade de Organizacional esteja incorreto ou que ninguém desta unidade tenha se cadastrado na lista telefônica.</p> | |
87 | + <p>Tente encontrar o ramal desejado diretamente na <a href="http://intranet.dpf.gov.br/servicos/lista-telefonica" target="_blank" title="Lista Telefônica">Lista Telefônica do Portal de Serviços da Intranet</a> ou caso a o nome da unidade esteja incorreto, entre em contato com a Central de Atendimento ao Usuário da CGTI.</p> | |
88 | + </div> | |
89 | + </div> | |
90 | + </tal:notunity> | |
91 | +</tal:items> | |
92 | + | |
93 | +</body> | |
94 | + | |
95 | +</html> | ... | ... |
pf/listatelefonica/browser/templates/unidadesjsonview.pt
0 → 100644
1 | +++ a/pf/listatelefonica/browser/templates/unidadesview.pt | |
... | ... | @@ -0,0 +1,91 @@ |
1 | +<tal:comment replace="nothing"> | |
2 | + Este template não está sendo utilizado no momento. | |
3 | + Ele está aqui pois a solicitação é de que o conteúdo exibido não contenha | |
4 | + topo, rodapé nem colunas. | |
5 | + Se necessário utilizar futuramente... está pronto =) | |
6 | +</tal:comment> | |
7 | + | |
8 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" | |
9 | + xmlns:tal="http://xml.zope.org/namespaces/tal" | |
10 | + xmlns:metal="http://xml.zope.org/namespaces/metal" | |
11 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" | |
12 | + lang="pt-br" | |
13 | + metal:use-macro="context/main_template/macros/master" | |
14 | + i18n:domain="pf.listatelefonica"> | |
15 | + | |
16 | +<metal:override fill-slot="top_slot" | |
17 | + tal:define="dummy python:request.set('disable_border', 1)" /> | |
18 | + | |
19 | + <tal:head metal:fill-slot="style_slot"> | |
20 | + <link rel="stylesheet" type="text/css" href="form_busca.css" media="screen" | |
21 | + tal:attributes="href string:${context/portal_url}/++resource++pf.listatelefonica/unidades.css"> | |
22 | + </tal:head> | |
23 | + | |
24 | +<body> | |
25 | + | |
26 | + <metal:main fill-slot="main"> | |
27 | + | |
28 | + <tal:items define="unidades view/persons"> | |
29 | + <tal:repeat condition="unidades" | |
30 | + repeat="unidade python:unidades[0]"> | |
31 | + <div class="row"> | |
32 | + <div class="cell width-full position-0 "> | |
33 | + <div class="tile azul ui-droppable"> | |
34 | + <div class="outstanding-header"> | |
35 | + <h3 class="outstanding-title" | |
36 | + tal:content="unidade|nothing">UNIDADE</h3> | |
37 | + </div> | |
38 | + </div> | |
39 | + </div> | |
40 | + </div> | |
41 | + <div class="row lista-person"> | |
42 | + <div class="cell width-full position-0 "> | |
43 | + <table class="listing" | |
44 | + tal:define="persons python:unidades[1]"> | |
45 | + <tbody> | |
46 | + <tr class="odd"> | |
47 | + <th>Nome</th> | |
48 | + <th>Cargo</th> | |
49 | + <th>Função</th> | |
50 | + <th>Email</th> | |
51 | + <th>Ramal</th> | |
52 | + <th>Celular</th> | |
53 | + <th>Voip</th> | |
54 | + </tr> | |
55 | + <tr class="even" | |
56 | + tal:condition="python: unidade in persons" | |
57 | + tal:repeat="person python:persons[unidade]"> | |
58 | + <td> | |
59 | + <a href="" title="" | |
60 | + tal:content="person/nome" | |
61 | + tal:attributes="href person/url; | |
62 | + title person/nome"> | |
63 | + Nome | |
64 | + </a> | |
65 | + </td> | |
66 | + <td tal:content="person/cargo">Cargo</td> | |
67 | + <td tal:content="person/funcao">Função</td> | |
68 | + <td tal:content="person/email">Email</td> | |
69 | + <td tal:content="structure person/ramal">Ramal</td> | |
70 | + <td tal:content="person/celular|nothing">Celular</td> | |
71 | + <td tal:content="person/voip|nothing">Voip</td> | |
72 | + </tr> | |
73 | + <tr class="odd" | |
74 | + tal:condition="not: persons"> | |
75 | + <td colspan="7"><span class="discreet">Nenhum registro encontrado</span></td> | |
76 | + </tr> | |
77 | + </tbody> | |
78 | + </table> | |
79 | + </div> | |
80 | + </div> | |
81 | + </tal:repeat> | |
82 | + <tal:notunity> | |
83 | + <p class="discreet" tal:condition="not: unidades">Nenhum resultado encontrado</p> | |
84 | + </tal:notunity> | |
85 | + </tal:items> | |
86 | + | |
87 | + </metal:main> | |
88 | + | |
89 | +</body> | |
90 | + | |
91 | +</html> | ... | ... |
1 | +++ a/pf/listatelefonica/browser/unidades.py | |
... | ... | @@ -0,0 +1,70 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | + | |
3 | + | |
4 | +class Unidade(object): | |
5 | + """ """ | |
6 | + def __init__(self, base): | |
7 | + assert isinstance(base, list), 'Only lists are accepted' | |
8 | + self.base = map(unicode, base) # [u'DPF', u'CGTI', u'DINF', u'SDS'] | |
9 | + | |
10 | + def _reverse_str(self, unity): | |
11 | + """ | |
12 | + Recebe uma unidade no formato string a inverte. | |
13 | + Ex.: | |
14 | + >>> self.reverse_str("SDS/DINF/CGTI/DPF") | |
15 | + >>> "DPF/CGTI/DINF/SDS" | |
16 | + """ | |
17 | + result = reversed(unity.split("/")) | |
18 | + return "/".join(result) | |
19 | + | |
20 | + def normalize(self): | |
21 | + """ | |
22 | + Retorna a unidade instanciada em self.base em string no formato invertido. | |
23 | + Ex.: | |
24 | + >>> self.base | |
25 | + >>> [u'DPF', u'CGTI', u'DINF', u'SDS'] | |
26 | + >>> self.normalize() | |
27 | + >>> "SDS/DINF/CGTI/DPF" | |
28 | + """ | |
29 | + return self._reverse_str("/".join(self.base)) | |
30 | + | |
31 | + def match(self, unities): | |
32 | + """ | |
33 | + Checa se todas as unidades em self.base casam com as | |
34 | + unidades passadas em 'unities' em ordem (inversa) e valor. | |
35 | + Ex.: | |
36 | + >>> self.base | |
37 | + >>> [u'DPF', u'CGTI', u'DINF', u'SDS'] | |
38 | + >>> self.match([u"SDS", u"DINF", u"GGTI", u"DPF"]) | |
39 | + >>> True | |
40 | + >>> self.match([u"DINF", u"SDS", u"GGTI", u"DPF"]) | |
41 | + >>> False | |
42 | + >>> self.match([u"DINF", u"GGTI", u"DPF"]) | |
43 | + >>> True | |
44 | + """ | |
45 | + check = [x for x in reversed(unities)] | |
46 | + result = map(lambda x: x[0] == x[1], zip(self.base, check)) | |
47 | + return all(result) | |
48 | + | |
49 | + def reverse(self, unities): | |
50 | + """ | |
51 | + Recebe uma lista de unidades e as retorna no formato reverso. | |
52 | + Ex.: | |
53 | + >>> self.reverse([u'SST/DINF/CGTI/DPF', u'SDS/DINF/CGTI/DPF']) | |
54 | + >>> [u'DPF/CGTI/DINF/SST', u'DPF/CGTI/DINF/SDS'] | |
55 | + """ | |
56 | + nl = map(lambda x: self._reverse_str(x), unities) | |
57 | + result = [x for x in nl] | |
58 | + return result | |
59 | + | |
60 | + def sort(self, unities): | |
61 | + """ | |
62 | + Recebe uma lista de unidades e as ordena em ordem alfabética | |
63 | + >>> unities = [u'SDS/DINF/CGTI/DPF', u'DINF/CGTI/DPF', u'GAB/CGTI/DPF', u'CGTI/DPF'] | |
64 | + >>> self.sort(unities) | |
65 | + >>> [u'CGTI/DPF', u'DINF/CGTI/DPF', u'SDS/DINF/CGTI/DPF', u'GAB/CGTI/DPF'] | |
66 | + """ | |
67 | + unities_l = self.reverse(unities) # Reverte as unidades | |
68 | + s_result = sorted(unities_l) # Ordena em ordem alfabética | |
69 | + result = self.reverse(s_result) # Resultado invertido | |
70 | + return result | ... | ... |
1 | +++ a/pf/listatelefonica/browser/views.py | |
... | ... | @@ -0,0 +1,233 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | + | |
3 | +import json | |
4 | + | |
5 | +from plone import api | |
6 | +from five import grok | |
7 | +from zope.interface import Interface | |
8 | +# from collections import OrderedDict | |
9 | + | |
10 | +from pf.listatelefonica.pessoa import IPessoa | |
11 | +from pf.listatelefonica.browser.unidades import Unidade | |
12 | + | |
13 | + | |
14 | +grok.templatedir('templates') | |
15 | + | |
16 | + | |
17 | +class ListaTelefonicaView(grok.View): | |
18 | + grok.context(Interface) | |
19 | + grok.require('zope2.View') | |
20 | + grok.name('listatelefonica_view') | |
21 | + | |
22 | + def _checkRegistry(self, username): | |
23 | + """ Verifica se o usuario ja possui um cadastro na lista telefonica e retorna este """ | |
24 | + # Search for data in portal_catalog tool | |
25 | + portal_url = self.context.absolute_url() | |
26 | + catalog = api.portal.get_tool(name='portal_catalog') | |
27 | + brains = catalog(portal_type="pf.listatelefonica.pessoa", | |
28 | + id=username, | |
29 | + Creator=username) | |
30 | + if brains: # if user data exists, returns url to edit | |
31 | + user_data = brains[0].getObject().absolute_url() | |
32 | + target = "{0}/edit".format(user_data) | |
33 | + return target | |
34 | + | |
35 | + target = "{0}/++add++pf.listatelefonica.pessoa".format(portal_url) | |
36 | + return target | |
37 | + | |
38 | + def registry(self): | |
39 | + """ Obtem o usuario logado no portal, verifica se o mesmo ja possui | |
40 | + cadastro e fornece a url para login/adicionar/editar """ | |
41 | + | |
42 | + # Check if actual user is logged on portal | |
43 | + if api.user.is_anonymous(): | |
44 | + # messages = IStatusMessage(self.request) | |
45 | + # messages.add(u'Para editar seus dados você precisa antes se identificar.', type=u'info') | |
46 | + # messages.add(u'entre com seu login e senha nos campos abaixo para continuar.', type=u'info') | |
47 | + portal_url = self.context.absolute_url() | |
48 | + login_url = "{0}/login_form?came_from=after_login".format(portal_url) | |
49 | + return login_url | |
50 | + | |
51 | + # Get username | |
52 | + current = api.user.get_current() | |
53 | + username = current.getUserName() | |
54 | + | |
55 | + target = self._checkRegistry(username) | |
56 | + | |
57 | + return target | |
58 | + | |
59 | + def get_description(self): | |
60 | + """ Verifica se existe um objeto do tipo 'Página' com o mesmo ID do diretório que se encontra | |
61 | + Se houver, retorna o corpo do texto deste objeto como descrição da Home. """ | |
62 | + folder_path = '/'.join(self.context.getPhysicalPath()) | |
63 | + catalog = api.portal.get_tool(name='portal_catalog') | |
64 | + brains = catalog(portal_type="Document", | |
65 | + id=self.context.id, | |
66 | + review_state='published', | |
67 | + path={'query': folder_path, 'depth': 1}, | |
68 | + sort_limit=1)[:1] | |
69 | + if brains: | |
70 | + obj = brains[0].getObject() | |
71 | + return obj.text.output | |
72 | + | |
73 | + return self.context.Description() | |
74 | + | |
75 | + | |
76 | +class PessoaView(grok.View): | |
77 | + grok.context(IPessoa) | |
78 | + grok.require('zope2.View') | |
79 | + grok.name('view') | |
80 | + | |
81 | + def get_ramais(self, context): | |
82 | + ramal1 = context.ramal1 or '' | |
83 | + ramal2 = context.ramal2 | |
84 | + | |
85 | + if not ramal2: | |
86 | + return "<span>{0}</span>".format(ramal1) | |
87 | + | |
88 | + return "<span>{0}</span> <br /> <span>{1}</span>".format(ramal1, ramal2) | |
89 | + | |
90 | + def is_owner(self): | |
91 | + # Get username | |
92 | + current = api.user.get_current() | |
93 | + username = current.getUserName() | |
94 | + | |
95 | + # Check if logged user is object's creator | |
96 | + owner = self.context.Creator() | |
97 | + if username == owner: | |
98 | + return True | |
99 | + return False | |
100 | + | |
101 | + | |
102 | +class UnidadesView(grok.View): | |
103 | + grok.name('unidade') | |
104 | + grok.require('zope2.View') | |
105 | + grok.context(Interface) | |
106 | + | |
107 | + def __call__(self): | |
108 | + query = self.request.get('q', '').upper() | |
109 | + if query.endswith('/'): | |
110 | + query = query[:-1] | |
111 | + self.unidade = Unidade(query.split('/')) | |
112 | + return super(UnidadesView, self).__call__() | |
113 | + | |
114 | + def brains(self): | |
115 | + pcatalog = api.portal.get_tool(name='portal_catalog') | |
116 | + path = '/'.join(api.portal.get().getPhysicalPath()) + '/lista-telefonica' | |
117 | + search = self.unidade.normalize().split('/') | |
118 | + query = { | |
119 | + 'portal_type': ['pf.listatelefonica.pessoa', ], | |
120 | + 'path': {'query': path, 'depth': 99}, | |
121 | + 'unidade': search, | |
122 | + 'sort_on': 'sortable_title', | |
123 | + 'sort_order': 'ascending', | |
124 | + } | |
125 | + results = pcatalog(**query) | |
126 | + return results | |
127 | + | |
128 | + def get_ramais(self, context): | |
129 | + ramal1 = context.ramal1 or '' | |
130 | + ramal2 = context.ramal2 | |
131 | + | |
132 | + if not ramal2: | |
133 | + return "<span>{0}</span>".format(ramal1) | |
134 | + | |
135 | + return "<span>{0}</span> <br /> <span>{1}</span>".format(ramal1, ramal2) | |
136 | + | |
137 | + def person2dict(self, obj, brain=None): | |
138 | + """ """ | |
139 | + people = [{ | |
140 | + 'nome': obj.nome, | |
141 | + 'cargo': obj.cargo, | |
142 | + 'funcao': obj.funcao, | |
143 | + 'email': "{0}@dpf.gov.br".format(obj.Title()), | |
144 | + 'ramal': self.get_ramais(obj), | |
145 | + 'celular': obj.celular, | |
146 | + 'voip': obj.voip, | |
147 | + 'url': brain.getURL() | |
148 | + }] | |
149 | + return people | |
150 | + | |
151 | + def get_results(self): | |
152 | + """ | |
153 | + Retorna uma lista com as unidades ordenadas e um dicionário Unidade/Pessoas. | |
154 | + Ex.: | |
155 | + [ | |
156 | + ["CGTI/DPF", "CGTI/DPF/DINF", "CGTI/DPF/DINF/SDS"], | |
157 | + { | |
158 | + "CGTI/DPF": [<Pessoa 1>, <Pessoa 2> ], | |
159 | + "CGTI/DPF/DINF/SDS": [<Pessoa 3>, <Pessoa 4>], | |
160 | + "CGTI/DPF/DINF": [<Pessoa 5>, <Pessoa 6>] | |
161 | + } | |
162 | + ] | |
163 | + """ | |
164 | + results = {} | |
165 | + unity_list = [] | |
166 | + | |
167 | + brains = self.brains() | |
168 | + if not brains: | |
169 | + return [] | |
170 | + | |
171 | + for brain in brains: | |
172 | + obj = brain.getObject() | |
173 | + | |
174 | + # Remove espaços em branco no nome de cada unidade | |
175 | + un = map(lambda x: x.strip(), obj.unidade.split('/')) | |
176 | + | |
177 | + # Checa se a unidade cadastrada pelo usuário casa com a unidade buscada na URL | |
178 | + match = self.unidade.match(un) | |
179 | + if not match: | |
180 | + continue | |
181 | + | |
182 | + people = self.person2dict(obj, brain) | |
183 | + | |
184 | + unity = '/'.join(un) | |
185 | + | |
186 | + # verifica se a unidade já é uma chave do dicionário results | |
187 | + if not (unity in results): | |
188 | + results[unity] = [] | |
189 | + | |
190 | + # verifica se a unidade já está na lista geral das unidades encontradas | |
191 | + if not (unity in unity_list): | |
192 | + unity_list.append(unity) | |
193 | + | |
194 | + results[unity] += people | |
195 | + | |
196 | + # Usado para tal:condition no template. Resultado default retorna: [[],{}] | |
197 | + if not unity_list: | |
198 | + return [] | |
199 | + | |
200 | + # Ordena as unidades de acordo com a árvore | |
201 | + unidades = self.unidade.sort(unity_list) | |
202 | + | |
203 | + return [unidades, results] | |
204 | + | |
205 | + def update(self): | |
206 | + self.persons = self.get_results() | |
207 | + | |
208 | + | |
209 | +class UnidadesIframeView(UnidadesView): | |
210 | + grok.name('unidade_iframe') | |
211 | + | |
212 | + | |
213 | +class UnidadesJsonView(UnidadesView): | |
214 | + grok.name('unidade_json') | |
215 | + | |
216 | + def get_ramais(self, context): | |
217 | + ramal1 = context.ramal1 or '' | |
218 | + ramal2 = context.ramal2 | |
219 | + | |
220 | + if not ramal2: | |
221 | + return "{0}".format(ramal1) | |
222 | + | |
223 | + return "{0} / {1}".format(ramal1, ramal2) | |
224 | + | |
225 | + def get_results(self): | |
226 | + results = super(UnidadesJsonView, self).get_results() | |
227 | + if results: | |
228 | + results.pop(0) | |
229 | + return results | |
230 | + | |
231 | + def update(self): | |
232 | + self.request.response.setHeader("Content-type", "application/json") | |
233 | + self.persons = json.dumps(self.get_results()) | ... | ... |
1 | +++ a/pf/listatelefonica/configure.zcml | |
... | ... | @@ -0,0 +1,38 @@ |
1 | +<configure | |
2 | + xmlns="http://namespaces.zope.org/zope" | |
3 | + xmlns:five="http://namespaces.zope.org/five" | |
4 | + xmlns:i18n="http://namespaces.zope.org/i18n" | |
5 | + xmlns:genericsetup="http://namespaces.zope.org/genericsetup" | |
6 | + xmlns:browser="http://namespaces.zope.org/browser" | |
7 | + xmlns:grok="http://namespaces.zope.org/grok" | |
8 | + xmlns:plone="http://namespaces.plone.org/plone" | |
9 | + i18n_domain="pf.listatelefonica"> | |
10 | + | |
11 | + <!-- Include configuration for dependencies listed in setup.py --> | |
12 | + <includeDependencies package="." /> | |
13 | + <include package=".browser" /> | |
14 | + | |
15 | + <!-- Grok the package to initialize schema interfaces and content classes --> | |
16 | + <grok:grok package="." /> | |
17 | + | |
18 | + <!-- Needed for perform uninstall handler --> | |
19 | + <five:registerPackage package="." /> | |
20 | + | |
21 | + <!-- Register a resource directory from which we can deploy static | |
22 | + resource files. --> | |
23 | + <browser:resourceDirectory | |
24 | + name="pf.listatelefonica" | |
25 | + directory="resources" /> | |
26 | + | |
27 | + <genericsetup:registerProfile | |
28 | + name="default" | |
29 | + title="pf.listatelefonica" | |
30 | + directory="profiles/default" | |
31 | + description="Installs the pf.listatelefonica package" | |
32 | + provides="Products.GenericSetup.interfaces.EXTENSION" | |
33 | + /> | |
34 | + <!-- -*- extra stuff goes here -*- --> | |
35 | + | |
36 | + <adapter factory=".indexes.sortable_title" name="sortable_title" /> | |
37 | + | |
38 | +</configure> | ... | ... |
1 | +++ a/pf/listatelefonica/macros/__init__.py | ... | ... |
pf/listatelefonica/macros/templates/listacadastroview.pt
0 → 100644
1 | +++ a/pf/listatelefonica/macros/templates/listacadastroview.pt | |
... | ... | @@ -0,0 +1,14 @@ |
1 | +<metal:block define-macro="cadastro"> | |
2 | +<div class="row" | |
3 | + tal:define="target nocall: view/registry|nothing" | |
4 | + tal:condition="target|nothing"> | |
5 | + <div class="cell width-full position-0 "> | |
6 | + <div class="tile azul ui-droppable" > | |
7 | + <div class="outstanding-header"> | |
8 | + <a href="" class="outstanding-link" title="Editar dados" | |
9 | + tal:attributes="href target">Editar dados</a> | |
10 | + </div> | |
11 | + </div> | |
12 | + </div> | |
13 | + </div> | |
14 | +</metal:block> | ... | ... |
pf/listatelefonica/macros/templates/listatelefonicabatch.pt
0 → 100644
1 | +++ a/pf/listatelefonica/macros/templates/listatelefonicabatch.pt | |
... | ... | @@ -0,0 +1,69 @@ |
1 | +<html xmlns="http://www.w3.org/1999/xhtml" | |
2 | + xmlns:tal="http://xml.zope.org/namespaces/tal" | |
3 | + xmlns:metal="http://xml.zope.org/namespaces/metal" | |
4 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" | |
5 | + i18n:domain="plone"> | |
6 | +<body> | |
7 | + | |
8 | +<tal:comment condition="nothing">You can feed in batch_base_url by enclosing | |
9 | +the metal:use-macro="context/batch_macros/macros/navigation" statement in your | |
10 | +template with a tal:define="batch_base_url YOUR_BASE_URL" tales expression. | |
11 | +</tal:comment> | |
12 | +<div align="right" | |
13 | + metal:define-macro="navigation" | |
14 | + tal:define="request request|context/request|container/request|nothing; | |
15 | + batch batch|nothing; | |
16 | + batchformkeys batchformkeys|nothing; | |
17 | + batchlinkparams python:batchformkeys and dict([(key, request.form[key]) for key in batchformkeys if key in request]) or request.form; | |
18 | + mq python:modules['ZTUtils'].make_query; | |
19 | + url batch_base_url | context/@@seopack_batch_url | request/ACTUAL_URL; | |
20 | + currentpage batch/pagenumber;" | |
21 | + tal:condition="python: batch.next or batch.previous"> | |
22 | + | |
23 | + <div class="numeros left"> | |
24 | + <tal:comment tal:replace="nothing"> | |
25 | + Current page | |
26 | + </tal:comment> | |
27 | + <span tal:condition="batch/navlist" class="current" | |
28 | + tal:content="batch/pagenumber">Current page number</span> | |
29 | + | |
30 | + <tal:comment tal:replace="nothing"> | |
31 | + Link to last | |
32 | + </tal:comment>de | |
33 | + <span tal:content="batch/numpages">3457</span> | |
34 | + </div> | |
35 | + | |
36 | + | |
37 | + <div class="setas right"> | |
38 | + | |
39 | + <span class="previous left" | |
40 | + tal:define="p batch/previous | nothing" | |
41 | + tal:condition="p"> | |
42 | + <a href="" id="previous" | |
43 | + tal:attributes="href python: '%s?%s' % (url , mq( batchlinkparams, {batch.b_start_str:p.first} ))"> | |
44 | + <span i18n:translate="batch_previous_x_items" tal:omit-tag=""></span> | |
45 | + </a> | |
46 | + </span> | |
47 | + | |
48 | + <span class="next right" | |
49 | + tal:define="n batch/next | nothing" | |
50 | + tal:condition="n"> | |
51 | + <a href="" id="next" | |
52 | + tal:attributes="href python: '%s?%s' % (url , mq( batchlinkparams, {batch.b_start_str:n.first} ))"> | |
53 | + <span i18n:translate="batch_next_x_items" tal:omit-tag=""></span> | |
54 | + </a> | |
55 | + </span> | |
56 | + </div> | |
57 | + | |
58 | + | |
59 | +</div> | |
60 | + | |
61 | +</body> | |
62 | +</html> | |
63 | + | |
64 | + | |
65 | + | |
66 | + | |
67 | + | |
68 | + | |
69 | + | ... | ... |
pf/listatelefonica/macros/templates/listatelefonicabuscaview.pt
0 → 100644
1 | +++ a/pf/listatelefonica/macros/templates/listatelefonicabuscaview.pt | |
... | ... | @@ -0,0 +1,222 @@ |
1 | +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" | |
2 | + xmlns:tal="http://xml.zope.org/namespaces/tal" | |
3 | + xmlns:metal="http://xml.zope.org/namespaces/metal" | |
4 | + xmlns:i18n="http://xml.zope.org/namespaces/i18n" | |
5 | + lang="en" | |
6 | + metal:use-macro="context/main_template/macros/master" | |
7 | + i18n:domain="pf.listatelefonica"> | |
8 | + | |
9 | + <metal:override fill-slot="top_slot" | |
10 | + tal:define="dummy python:request.set('disable_border', 1)" /> | |
11 | + | |
12 | + <tal:head metal:fill-slot="style_slot"> | |
13 | + <link rel="stylesheet" type="text/css" href="form_busca.css" media="screen" | |
14 | + tal:attributes="href string:${context/portal_url}/++resource++pf.listatelefonica/form_busca.css"> | |
15 | + </tal:head> | |
16 | + | |
17 | +<body> | |
18 | + | |
19 | +<metal:main fill-slot="main"> | |
20 | + | |
21 | + <!-- Login / Cadastrar / Editar --> | |
22 | + <metal:block use-macro="context/listacadastro_view/macros/cadastro" /> | |
23 | + | |
24 | + <div class="row"> | |
25 | + <div class="cell width-full position-0 "> | |
26 | + <div class="tile azul ui-droppable" > | |
27 | + <div class="outstanding-header"> | |
28 | + <h2 class="outstanding-title" tal:content="context/Title">Bem vindo ao Portal de Serviços da Intranet</h2> | |
29 | + </div> | |
30 | + </div> | |
31 | + </div> | |
32 | + </div> | |
33 | + <div class="row"> | |
34 | + <div class="cell width-full position-0 "> | |
35 | + <div tal:content="context/Description"></div> | |
36 | + </div> | |
37 | + </div> | |
38 | + | |
39 | + <metal:macro metal:define-macro="busca"> | |
40 | + | |
41 | + <div class="row" id="form-busca"> | |
42 | + <div class="cell width-full position-2 " > | |
43 | + | |
44 | + <script type="text/javascript"> | |
45 | + | |
46 | + $(document).ready(function(){ | |
47 | + $('#open_busca_avancada span').click(function() { | |
48 | + $('#busca_avancada').toggle(); | |
49 | + $('#open_busca_avancada span').toggle(); | |
50 | + }); | |
51 | + }); | |
52 | + </script> | |
53 | + | |
54 | + <form method="POST" action="" tal:attributes="action string:${context/absolute_url}/listatelefonica_busca_view"> | |
55 | + | |
56 | + <div class="field ArchetypesStringWidget no-marging "> | |
57 | + <input type="text" value="" | |
58 | + placeholder="Buscar na Lista Telefônica" | |
59 | + name="title" | |
60 | + tal:attributes="value request/title|string:"/> | |
61 | + <input type="submit" name="submited" value="OK"/> | |
62 | + </div> | |
63 | + | |
64 | + <div id="busca_avancada" style="display:none"> | |
65 | + <div class="field ArchetypesStringWidget "> | |
66 | + <label class="formQuestion" for="nome">Nome: | |
67 | + <span class="formHelp"></span></label> | |
68 | + <input type="text" size="80" value="" | |
69 | + name="nome" | |
70 | + tal:attributes="value request/nome|string:" /> | |
71 | + </div> | |
72 | + <div class="field ArchetypesStringWidget "> | |
73 | + <label class="formQuestion" for="telefone">Telefone: | |
74 | + <span class="formHelp"></span></label> | |
75 | + <input type="text" size="80" value="" id="widgets-ramal1" | |
76 | + name="telefone" | |
77 | + tal:attributes="value request/telefone|string:"/> | |
78 | + </div> | |
79 | + <div class="field ArchetypesStringWidget "> | |
80 | + <label class="formQuestion" for="voip">VOIP: | |
81 | + <span class="formHelp"></span></label> | |
82 | + <input type="text" size="80" value="" | |
83 | + id="form-widgets-voip" | |
84 | + name="voip" | |
85 | + tal:attributes="value request/voip|string:"/> | |
86 | + </div> | |
87 | + <div class="field ArchetypesStringWidget "> | |
88 | + <label class="formQuestion" | |
89 | + for="unidade">Unidade: | |
90 | + <span class="formHelp"></span></label> | |
91 | + <input type="text" size="80" value="" | |
92 | + placeholder="ex: SDS/DINF/CGTI/DPF" | |
93 | + name="unidade" | |
94 | + tal:attributes="value request/unidade|string:"/> | |
95 | + </div> | |
96 | + <div class="field ArchetypesStringWidget "> | |
97 | + <label class="formQuestion" | |
98 | + for="cargo">Cargo: | |
99 | + <span class="formHelp"></span></label> | |
100 | + <input type="text" size="80" value="" | |
101 | + name="cargo" | |
102 | + tal:attributes="value request/cargo|string:"/> | |
103 | + </div> | |
104 | + <div class="field ArchetypesStringWidget "> | |
105 | + <label class="formQuestion" | |
106 | + for="funcao">Função: | |
107 | + <span class="formHelp"></span></label> | |
108 | + <input type="text" size="80" value="" | |
109 | + name="funcao" | |
110 | + tal:attributes="value request/funcao|string:"/> | |
111 | + </div> | |
112 | + | |
113 | + </div> | |
114 | + | |
115 | + <div id="open_busca_avancada"> | |
116 | + <span id="more">+ Busca Avançada</span> | |
117 | + <span id="simple" style="display:none">+ Busca Simples</span> | |
118 | + | |
119 | + </div> | |
120 | + | |
121 | + </form> | |
122 | + <br /> | |
123 | + </div> | |
124 | + </div> | |
125 | + | |
126 | + </metal:macro> | |
127 | + | |
128 | + <div id="content-itens"> | |
129 | + <metal:macro metal:define-macro="page-view"> | |
130 | + <tal:def tal:define="Batch python:modules['Products.CMFPlone'].Batch; | |
131 | + mq python:modules['ZTUtils'].make_query; | |
132 | + url context/@@seopack_batch_url | request/ACTUAL_URL; | |
133 | + batchformkeys batchformkeys|nothing; | |
134 | + batchlinkparams python:batchformkeys and dict([(key, request.form[key]) for key in batchformkeys if key in request]) or request.form; | |
135 | + | |
136 | + b_size request/b_size|string:10; | |
137 | + b_start python:0; | |
138 | + b_start request/b_start | b_start; | |
139 | + itens view/results|python:[]; | |
140 | + batch python:Batch(itens, int(b_size), int(b_start), pagerange=len(itens));"> | |
141 | + | |
142 | + <input type="hidden" id="b_size" tal:attributes="value b_size" /> | |
143 | + <input type="hidden" id="b_start" tal:attributes="value b_start" /> | |
144 | + <metal:macro metal:define-macro="navegacao-macro"> | |
145 | + <div id="paginacao"> | |
146 | + | |
147 | + <div class="row"> | |
148 | + <div id="size-nav" class="cell width-8 position-0"> | |
149 | + <p> | |
150 | + <span tal:content="python:'Total: ('+str(len(itens))+')'">Total (XX)</span> | |
151 | + | | |
152 | + <span tal:define="css python:'font-weight:bolder;;'">Itens por pagina: | |
153 | + <a class="c-pointer" id="itenspage" | |
154 | + tal:attributes="style python:b_size == '10' and css or ''; | |
155 | + href python: '%s?%s' % (url , mq( batchlinkparams, {'b_size':'10'} ))">10</a>, | |
156 | + <a class="c-pointer" id="itenspage" | |
157 | + tal:attributes="style python:b_size == '20' and css or ''; | |
158 | + href python: '%s?%s' % (url , mq( batchlinkparams, {'b_size':'20'} ))">20</a>, | |
159 | + <a class="c-pointer" id="itenspage" | |
160 | + tal:attributes="style python:b_size == '30' and css or ''; | |
161 | + href python: '%s?%s' % (url , mq( batchlinkparams, {'b_size':'30'} ))">30</a>, | |
162 | + <a class="c-pointer" id="itenspage" | |
163 | + tal:attributes="style python:b_size == '40' and css or ''; | |
164 | + href python: '%s?%s' % (url , mq( batchlinkparams, {'b_size':'40'} ))">40</a> | |
165 | + </span> | |
166 | + </p> | |
167 | + </div> | |
168 | + <div id="pag-nav" class="cell width-8 position-8 "> | |
169 | + <metal:block metal:use-macro="context/listatelefonica_batch/macros/navigation" /> | |
170 | + </div> | |
171 | + </div> | |
172 | + </div> | |
173 | + <div class="visualClear"></div> | |
174 | + </metal:macro> | |
175 | + | |
176 | + <div class="visualClear"></div> | |
177 | + <metal:macro define-macro="news-list"> | |
178 | + <div id="news"> | |
179 | + | |
180 | + <div class="row"> | |
181 | + <div class="cell width-full position-0 "> | |
182 | + <table class="listing"> | |
183 | + <tbody> | |
184 | + <tr class="odd"> | |
185 | + <th>Nome</th> | |
186 | + <th>Unidade</th> | |
187 | + <th>Email</th> | |
188 | + <th>Ramal</th> | |
189 | + <th>Celular</th> | |
190 | + <th>Voip</th> | |
191 | + </tr> | |
192 | + <tal:rep repeat="item batch"> | |
193 | + <tr class="even"> | |
194 | + <td> | |
195 | + <a href="" | |
196 | + tal:content="item/nome" | |
197 | + tal:attributes="href item/url">Nome</a> | |
198 | + </td> | |
199 | + <td tal:content="item/unidade">Unidade</td> | |
200 | + <td tal:content="item/email">Email</td> | |
201 | + <td tal:content="structure item/ramais">Ramal</td> | |
202 | + <td tal:content="item/celular|nothing">Celular</td> | |
203 | + <td tal:content="item/voip|nothing">Voip</td> | |
204 | + </tr> | |
205 | + </tal:rep> | |
206 | + </tbody> | |
207 | + </table> | |
208 | + </div> | |
209 | + </div> | |
210 | + <div class="visualClear"></div> | |
211 | + </div> | |
212 | + </metal:macro> | |
213 | + | |
214 | + <metal:block metal:use-macro="context/listatelefonica_busca_view/macros/navegacao-macro" /> | |
215 | + | |
216 | + </tal:def> | |
217 | + </metal:macro> | |
218 | + </div> | |
219 | + | |
220 | +</metal:main> | |
221 | +</body> | |
222 | +</html> | ... | ... |
1 | +++ a/pf/listatelefonica/macros/views.py | |
... | ... | @@ -0,0 +1,160 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | + | |
3 | +import re | |
4 | + | |
5 | +from plone import api | |
6 | +from five import grok | |
7 | + | |
8 | +from zope.interface import Interface | |
9 | + | |
10 | +from Products import AdvancedQuery | |
11 | + | |
12 | +from pf.listatelefonica.browser.views import ListaTelefonicaView | |
13 | + | |
14 | + | |
15 | +grok.templatedir('templates') | |
16 | + | |
17 | + | |
18 | +class ListaCadastroView(grok.View): | |
19 | + grok.context(Interface) | |
20 | + grok.require('zope2.View') | |
21 | + grok.name('listacadastro_view') | |
22 | + | |
23 | + | |
24 | +class ListaTelefonicaBatch(grok.View): | |
25 | + grok.context(Interface) | |
26 | + grok.require('zope2.View') | |
27 | + grok.name('listatelefonica_batch') | |
28 | + | |
29 | + | |
30 | +class AfterLoginHandler(grok.View): | |
31 | + """ classe responsável por verificar se o usuário possui cadastro após o login | |
32 | + quando o usuário clica em 'editar dados' e o direciona para o local adequado """ | |
33 | + grok.context(Interface) | |
34 | + grok.require('zope2.View') | |
35 | + grok.name('after_login') | |
36 | + | |
37 | + def _checkRegistry(self, username): | |
38 | + """ Verifica se o usuario ja possui um cadastro na lista telefonica e retorna este """ | |
39 | + # Search for data in portal_catalog tool | |
40 | + portal_url = self.context.absolute_url() | |
41 | + catalog = api.portal.get_tool(name='portal_catalog') | |
42 | + brains = catalog(portal_type="pf.listatelefonica.pessoa", | |
43 | + id=username, | |
44 | + Creator=username) | |
45 | + if brains: # if user data exists, returns url to edit | |
46 | + user_data = brains[0].getObject().absolute_url() | |
47 | + target = "{0}/edit".format(user_data) | |
48 | + return target | |
49 | + | |
50 | + target = "{0}/++add++pf.listatelefonica.pessoa".format(portal_url) | |
51 | + return target | |
52 | + | |
53 | + def render(self): | |
54 | + portal_url = self.context.absolute_url() | |
55 | + | |
56 | + if api.user.is_anonymous(): | |
57 | + self.redirect(portal_url) | |
58 | + | |
59 | + # Get username | |
60 | + current = api.user.get_current() | |
61 | + username = current.getUserName() | |
62 | + | |
63 | + target = self._checkRegistry(username) | |
64 | + self.redirect(target) | |
65 | + | |
66 | + | |
67 | +class ListaTelefonicaBuscaView(ListaTelefonicaView): | |
68 | + grok.name('listatelefonica_busca_view') | |
69 | + | |
70 | + results = [] | |
71 | + | |
72 | + def _parse_query_string(self, string): | |
73 | + """ Remove strings vazias e caracteres não aceitos na busca """ | |
74 | + pattern = re.compile("[^\w]|_") | |
75 | + result = pattern.sub('', string) | |
76 | + return result | |
77 | + | |
78 | + def result2dict(self, brains): | |
79 | + results = [] | |
80 | + for brain in brains: | |
81 | + obj = brain.getObject() | |
82 | + results.append({ | |
83 | + 'nome': obj.nome, | |
84 | + 'email': "{0}@dpf.gov.br".format(obj.title), | |
85 | + 'url': brain.getURL, | |
86 | + 'unidade': obj.unidade, | |
87 | + 'ramais': self.get_ramais(obj), | |
88 | + 'celular': obj.celular, | |
89 | + 'voip': obj.voip, | |
90 | + 'cargo': obj.cargo, | |
91 | + 'funcao': obj.funcao | |
92 | + }) | |
93 | + return results | |
94 | + | |
95 | + def get_ramais(self, item): | |
96 | + ramal1 = item.ramal1 or '' | |
97 | + ramal2 = item.ramal2 | |
98 | + | |
99 | + if not ramal2: | |
100 | + return "<span>{0}</span>".format(ramal1) | |
101 | + | |
102 | + return "<span>{0}</span> <br /> <span>{1}</span>".format(ramal1, ramal2) | |
103 | + | |
104 | + def advanced_search(self, form, fields=[]): | |
105 | + """ """ | |
106 | + if not fields: | |
107 | + return | |
108 | + | |
109 | + path = '/'.join(self.context.getPhysicalPath()) | |
110 | + query = AdvancedQuery.Eq('portal_type', 'pf.listatelefonica.pessoa') & \ | |
111 | + AdvancedQuery.Eq('path', path) | |
112 | + | |
113 | + for field in fields: | |
114 | + # val = self._parse_query_string(form.get(field)) | |
115 | + val = form.get(field) | |
116 | + if val: | |
117 | + search_ = val.split(' ') | |
118 | + search = filter(self._parse_query_string, search_) | |
119 | + search = ' '.join(search) | |
120 | + if field == 'telefone': | |
121 | + telefone = (AdvancedQuery.Eq('ramal1', search) | | |
122 | + AdvancedQuery.Eq('ramal2', search) | | |
123 | + AdvancedQuery.Eq('celular', search)) | |
124 | + | |
125 | + query = query & telefone | |
126 | + continue | |
127 | + query = query & AdvancedQuery.Eq(field, search) | |
128 | + return query | |
129 | + | |
130 | + def update(self): | |
131 | + """ """ | |
132 | + form = self.request.form | |
133 | + pcatalog = api.portal.get_tool(name='portal_catalog') | |
134 | + | |
135 | + if 'submited' in form.keys(): | |
136 | + | |
137 | + # Simple search | |
138 | + title = form.get('title', '') | |
139 | + if title: | |
140 | + search_ = title.split(' ') | |
141 | + search = filter(self._parse_query_string, search_) | |
142 | + path = '/'.join(self.context.getPhysicalPath()) | |
143 | + query = { | |
144 | + 'portal_type': ['pf.listatelefonica.pessoa', ], | |
145 | + 'path': {'query': path, 'depth': 99}, | |
146 | + 'SearchableText': search, | |
147 | + 'sort_on': 'sortable_title', | |
148 | + 'sort_order': 'ascending', | |
149 | + } | |
150 | + results = pcatalog(**query) | |
151 | + | |
152 | + # Advanced search | |
153 | + else: | |
154 | + fields = ['telefone', 'nome', 'voip', | |
155 | + 'unidade', 'cargo', 'funcao'] | |
156 | + query = self.advanced_search(form, fields) | |
157 | + sort_order = (('sortable_title', 'ascending'), ) | |
158 | + results = pcatalog.evalAdvancedQuery(query, sort_order) | |
159 | + | |
160 | + self.results = self.result2dict(results) | ... | ... |
1 | +++ a/pf/listatelefonica/pessoa.py | |
... | ... | @@ -0,0 +1,137 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | + | |
3 | +from plone import api | |
4 | +from zope import schema | |
5 | +from plone.dexterity.content import Item | |
6 | +from plone.supermodel import model | |
7 | +from plone.directives import form | |
8 | +from five import grok | |
9 | + | |
10 | +from zope.lifecycleevent.interfaces import IObjectCreatedEvent, IObjectModifiedEvent | |
11 | +from collective import dexteritytextindexer | |
12 | + | |
13 | + | |
14 | +class IPessoa(model.Schema): | |
15 | + """ | |
16 | + Pessoa - dados de uma pessoa para lista telefonica | |
17 | + """ | |
18 | + # Email is readonly only for display at new register. | |
19 | + # Its don't used in others code's parts | |
20 | + form.mode(email='display') | |
21 | + email = schema.TextLine( | |
22 | + title=u"Email", | |
23 | + description=u"", | |
24 | + required=False, | |
25 | + default=u"", | |
26 | + ) | |
27 | + | |
28 | + dexteritytextindexer.searchable('title') | |
29 | + form.mode(title='hidden') | |
30 | + title = schema.TextLine( | |
31 | + title=u"User ID", | |
32 | + description=u'ID do usuario - Obtido através do LDAP', | |
33 | + required=False | |
34 | + ) | |
35 | + | |
36 | + dexteritytextindexer.searchable('nome') | |
37 | + nome = schema.TextLine( | |
38 | + title=u" Nome", | |
39 | + description=u"Informe seu nome", | |
40 | + required=True | |
41 | + ) | |
42 | + | |
43 | + dexteritytextindexer.searchable('ramal1') | |
44 | + ramal1 = schema.TextLine( | |
45 | + title=u"Telefone 1", | |
46 | + description=u"Informe seu telefone de contato. Ex.: (22) 2222-2222", | |
47 | + required=True | |
48 | + ) | |
49 | + | |
50 | + dexteritytextindexer.searchable('ramal2') | |
51 | + ramal2 = schema.TextLine( | |
52 | + title=u"Telefone 2", | |
53 | + description=u"Informe outro número, se houver. Ex.: (33) 3333-3333", | |
54 | + required=False | |
55 | + ) | |
56 | + | |
57 | + dexteritytextindexer.searchable('celular') | |
58 | + celular = schema.TextLine( | |
59 | + title=u"Celular", | |
60 | + description=u"Informe seu telefone celular Ex.: (44) 4444-44444 ", | |
61 | + required=False | |
62 | + ) | |
63 | + | |
64 | + dexteritytextindexer.searchable('voip') | |
65 | + voip = schema.TextLine( | |
66 | + title=u"VOIP", | |
67 | + description=u"Informe o número VOIP. Ex.: 4 2222 2222", | |
68 | + required=False | |
69 | + ) | |
70 | + | |
71 | + dexteritytextindexer.searchable('unidade') | |
72 | + unidade = schema.TextLine( | |
73 | + title=u"Unidade", | |
74 | + description=u"Informe a unidade. Ex.: SDS/DINF/CGTI/DPF", | |
75 | + required=True | |
76 | + ) | |
77 | + | |
78 | + dexteritytextindexer.searchable('cargo') | |
79 | + cargo = schema.TextLine( | |
80 | + title=u"Cargo", | |
81 | + description=u"Informe seu cargo", | |
82 | + required=True | |
83 | + ) | |
84 | + | |
85 | + dexteritytextindexer.searchable('funcao') | |
86 | + funcao = schema.TextLine( | |
87 | + title=u"Função", | |
88 | + description=u"Informe sua função", | |
89 | + required=False | |
90 | + ) | |
91 | + | |
92 | + | |
93 | +@form.default_value(field=IPessoa['email']) | |
94 | +def default_email(data): | |
95 | + if api.user.is_anonymous(): | |
96 | + return u"" | |
97 | + current = api.user.get_current() | |
98 | + username = current.getUserName() | |
99 | + return "{0}@dpf.gov.br".format(username) | |
100 | + | |
101 | + | |
102 | +class Pessoa(Item): | |
103 | + grok.implements(IPessoa) | |
104 | + | |
105 | + def get_user_id(self): | |
106 | + # Get username | |
107 | + if api.user.is_anonymous(): | |
108 | + return 'isAnon' | |
109 | + | |
110 | + current = api.user.get_current() | |
111 | + username = current.getUserName() | |
112 | + return username | |
113 | + | |
114 | + | |
115 | +def adjust_display_text(object): | |
116 | + object.cargo = object.cargo.upper() | |
117 | + if object.funcao: | |
118 | + object.funcao = object.funcao.title() | |
119 | + object.unidade = object.unidade.upper() | |
120 | + return | |
121 | + | |
122 | + | |
123 | +@grok.subscribe(IPessoa, IObjectCreatedEvent) | |
124 | +def set_user_id(object, event): | |
125 | + """ O ID do objeto é gerado através do campo 'title' preenchido | |
126 | + automaticamente com o id obtido do LDAP """ | |
127 | + object.title = object.get_user_id() | |
128 | + object.email = "{0}@dpf.gov.br".format(object.get_user_id()) | |
129 | + object.exclude_from_nav = True | |
130 | + adjust_display_text(object) | |
131 | + return | |
132 | + | |
133 | + | |
134 | +@grok.subscribe(IPessoa, IObjectModifiedEvent) | |
135 | +def set_display_text(object, event): | |
136 | + adjust_display_text(object) | |
137 | + return | ... | ... |
1 | +++ a/pf/listatelefonica/profiles/default/import_steps.xml | |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<?xml version="1.0"?> | |
2 | +<import-steps> | |
3 | + <import-step id="pf.listatelefonica.install_index" | |
4 | + version="1.0.1" | |
5 | + handler="pf.listatelefonica.setuphandlers.install_index" | |
6 | + title="Create Index Catalog"> | |
7 | + </import-step> | |
8 | + <import-step id="pf.listatelefonica.create_initial_content" | |
9 | + version="1.0.0" | |
10 | + handler="pf.listatelefonica.setuphandlers.create_initial_content" | |
11 | + title="Create Initial Content"> | |
12 | + </import-step> | |
13 | +</import-steps> | ... | ... |
1 | +++ a/pf/listatelefonica/profiles/default/jsregistry.xml | |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<?xml version="1.0"?> | |
2 | +<object name="portal_javascripts"> | |
3 | + | |
4 | + <!-- pf.listatelefonica masks --> | |
5 | + <javascript | |
6 | + id="++resource++pf.listatelefonica/pf.listatelefonica.masks.js" | |
7 | + authenticated="False" | |
8 | + cacheable="True" compression="safe" cookable="True" | |
9 | + enabled="True" expression="" | |
10 | + inline="False" | |
11 | + /> | |
12 | + | |
13 | +</object> | ... | ... |
1 | +++ a/pf/listatelefonica/profiles/default/types.xml | |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<?xml version="1.0"?> | |
2 | +<!-- This file registers new types with portal_types. The types are | |
3 | + then configured with the corresponding files in types/*.xml. Note | |
4 | + that spaces are allowed in type names, but the corresponding XML file | |
5 | + uses an underscore instead. The "Factory-based Type Information with | |
6 | + dynamic views" refers to an FTI from Products.CMFDynamicViewFTI, | |
7 | + which supports Plone's "display" menu. | |
8 | + --> | |
9 | +<object name="portal_types" meta_type="Plone Types Tool"> | |
10 | + <!-- -*- extra stuff goes here -*- --> | |
11 | + <object name="pf.listatelefonica.pessoa" meta_type="Dexterity FTI" /> | |
12 | +</object> | |
13 | + | ... | ... |
pf/listatelefonica/profiles/default/types/import_steps.xml
0 → 100755
1 | +++ a/pf/listatelefonica/profiles/default/types/import_steps.xml | |
... | ... | @@ -0,0 +1,9 @@ |
1 | +<?xml version="1.0"?> | |
2 | +<import-steps> | |
3 | + <import-step id="pf.biblioteca.install_index" | |
4 | + version="1.0" | |
5 | + handler="pf.biblioteca.setuphandlers.install_index" | |
6 | + title="Create Index Catalog"> | |
7 | + | |
8 | + </import-step> | |
9 | +</import-steps> | ... | ... |
pf/listatelefonica/profiles/default/types/pf.listatelefonica.pessoa.xml
0 → 100644
1 | +++ a/pf/listatelefonica/profiles/default/types/pf.listatelefonica.pessoa.xml | |
... | ... | @@ -0,0 +1,57 @@ |
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<object name="pf.listatelefonica.pessoa" | |
3 | + meta_type="Dexterity FTI" | |
4 | + i18n:domain="pf.listatelefonica" xmlns:i18n="http://xml.zope.org/namespaces/i18n"> | |
5 | + | |
6 | + <!-- Basic metadata --> | |
7 | + <property name="title" i18n:translate="">Pessoa - Lista Telefônica</property> | |
8 | + <property name="description" | |
9 | + i18n:translate="">Pessoa - Dados do usuário para Lista Telefônica</property> | |
10 | + <property name="icon_expr">string:${portal_url}/Form.gif</property> | |
11 | + <property name="factory">pf.listatelefonica.pessoa</property> | |
12 | + <property name="global_allow">True</property> | |
13 | + <property name="filter_content_types">False</property> | |
14 | + <property name="allowed_content_types" /> | |
15 | + <property name="allow_discussion">False</property> | |
16 | + | |
17 | + <!-- schema and class used for content items --> | |
18 | + <property name="schema">pf.listatelefonica.pessoa.IPessoa</property> | |
19 | + <property name="klass">pf.listatelefonica.pessoa.Pessoa</property> | |
20 | + | |
21 | + <property name="behaviors"> | |
22 | + <element value="plone.app.content.interfaces.INameFromTitle" /> | |
23 | + <element value="plone.app.dexterity.behaviors.exclfromnav.IExcludeFromNavigation" /> | |
24 | + <element value="collective.dexteritytextindexer.behavior.IDexterityTextIndexer" /> | |
25 | + </property> | |
26 | + | |
27 | + <!-- View information --> | |
28 | + <property name="link_target"></property> | |
29 | + <property name="immediate_view">view</property> | |
30 | + <property name="default_view">view</property> | |
31 | + <property name="view_methods"> | |
32 | + <element value="view"/> | |
33 | + </property> | |
34 | + <property name="default_view_fallback">False</property> | |
35 | + <property name="add_permission">cmf.AddPortalContent</property> | |
36 | + | |
37 | + | |
38 | + <!-- Method aliases --> | |
39 | + <alias from="(Default)" to="(dynamic view)" /> | |
40 | + <alias from="view" to="(selected layout)" /> | |
41 | + <alias from="edit" to="@@edit" /> | |
42 | + | |
43 | + <alias from="sharing" to="@@sharing" /> | |
44 | + | |
45 | + <!-- Actions --> | |
46 | + <action title="View" action_id="view" category="object" condition_expr="" | |
47 | + url_expr="string:${object_url}/" visible="True"> | |
48 | + <permission value="View" /> | |
49 | +</action> | |
50 | + | |
51 | + <action title="Edit" action_id="edit" category="object" condition_expr="" | |
52 | + url_expr="string:${object_url}/edit" visible="True"> | |
53 | + <permission value="Manage Portal" /> | |
54 | + </action> | |
55 | + | |
56 | +</object> | |
57 | + | ... | ... |
1 | +++ a/pf/listatelefonica/profiles/default/workflows.xml | |
... | ... | @@ -0,0 +1,8 @@ |
1 | +<?xml version="1.0"?> | |
2 | +<object name="portal_workflow" meta_type="Plone Workflow Tool"> | |
3 | + <bindings> | |
4 | + <type type_id="pf.listatelefonica.pessoa"> | |
5 | + <bound-workflow workflow_id="one_state_workflow"/> | |
6 | + </type> | |
7 | + </bindings> | |
8 | +</object> | ... | ... |
1 | +++ a/pf/listatelefonica/resources/jquery.mask.min.js | |
... | ... | @@ -0,0 +1,12 @@ |
1 | +// jQuery Mask Plugin v1.7.7 | |
2 | +// github.com/igorescobar/jQuery-Mask-Plugin | |
3 | +(function(f){"function"===typeof define&&define.amd?define(["jquery"],f):f(window.jQuery||window.Zepto)})(function(f){var A=function(a,d,b){var h=this,m,p;a=f(a);d="function"===typeof d?d(a.val(),void 0,a,b):d;var c={getCaret:function(){try{var e,l=0,c=a.get(0),g=document.selection,d=c.selectionStart;if(g&&!~navigator.appVersion.indexOf("MSIE 10"))e=g.createRange(),e.moveStart("character",a.is("input")?-a.val().length:-a.text().length),l=e.text.length;else if(d||"0"===d)l=d;return l}catch(b){}},setCaret:function(e){try{if(a.is(":focus")){var l, | |
4 | +c=a.get(0);c.setSelectionRange?c.setSelectionRange(e,e):c.createTextRange&&(l=c.createTextRange(),l.collapse(!0),l.moveEnd("character",e),l.moveStart("character",e),l.select())}}catch(g){}},events:function(){a.on("keydown.mask",function(){m=c.val()}).on("keyup.mask",c.behaviour).on("paste.mask drop.mask",function(){setTimeout(function(){a.keydown().keyup()},100)}).on("change.mask",function(){a.data("changed",!0)}).on("blur.mask",function(){m===a.val()||a.data("changed")||a.trigger("change");a.data("changed", | |
5 | +!1)}).on("focusout.mask",function(){b.clearIfNotMatch&&!p.test(c.val())&&c.val("")})},getRegexMask:function(){for(var e=[],a,c,g,b,k=0;k<d.length;k++)(a=h.translation[d[k]])?(c=a.pattern.toString().replace(/.{1}$|^.{1}/g,""),g=a.optional,(a=a.recursive)?(e.push(d[k]),b={digit:d[k],pattern:c}):e.push(g||a?c+"?":c)):e.push(d[k].replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&"));e=e.join("");b&&(e=e.replace(new RegExp("("+b.digit+"(.*"+b.digit+")?)"),"($1)?").replace(new RegExp(b.digit,"g"),b.pattern));return new RegExp(e)}, | |
6 | +destroyEvents:function(){a.off("keydown keyup paste drop change blur focusout DOMNodeInserted ".split(" ").join(".mask ")).removeData("changeCalled")},val:function(e){var c=a.is("input");return 0<arguments.length?c?a.val(e):a.text(e):c?a.val():a.text()},getMCharsBeforeCount:function(e,a){for(var c=0,b=0,f=d.length;b<f&&b<e;b++)h.translation[d.charAt(b)]||(e=a?e+1:e,c++);return c},caretPos:function(e,a,b,g){return h.translation[d.charAt(Math.min(e-1,d.length-1))]?Math.min(e+b-a-g,b):c.caretPos(e+1, | |
7 | +a,b,g)},behaviour:function(a){a=a||window.event;var b=a.keyCode||a.which;if(-1===f.inArray(b,h.byPassKeys)){var d=c.getCaret(),g=c.val(),t=g.length,k=d<t,m=c.getMasked(),n=m.length,p=c.getMCharsBeforeCount(n-1)-c.getMCharsBeforeCount(t-1);m!==g&&c.val(m);!k||65===b&&a.ctrlKey||(8!==b&&46!==b&&(d=c.caretPos(d,t,n,p)),c.setCaret(d));return c.callbacks(a)}},getMasked:function(a){var l=[],f=c.val(),g=0,m=d.length,k=0,p=f.length,n=1,u="push",r=-1,q,v;b.reverse?(u="unshift",n=-1,q=0,g=m-1,k=p-1,v=function(){return-1< | |
8 | +g&&-1<k}):(q=m-1,v=function(){return g<m&&k<p});for(;v();){var w=d.charAt(g),x=f.charAt(k),s=h.translation[w];if(s)x.match(s.pattern)?(l[u](x),s.recursive&&(-1===r?r=g:g===q&&(g=r-n),q===r&&(g-=n)),g+=n):s.optional&&(g+=n,k-=n),k+=n;else{if(!a)l[u](w);x===w&&(k+=n);g+=n}}a=d.charAt(q);m!==p+1||h.translation[a]||l.push(a);return l.join("")},callbacks:function(e){var f=c.val(),h=f!==m;if(!0===h&&"function"===typeof b.onChange)b.onChange(f,e,a,b);if(!0===h&&"function"===typeof b.onKeyPress)b.onKeyPress(f, | |
9 | +e,a,b);if("function"===typeof b.onComplete&&f.length===d.length)b.onComplete(f,e,a,b)}};h.mask=d;h.options=b;h.remove=function(){var b;c.destroyEvents();c.val(h.getCleanVal()).removeAttr("maxlength");b=c.getCaret();c.setCaret(b-c.getMCharsBeforeCount(b));return a};h.getCleanVal=function(){return c.getMasked(!0)};h.init=function(){b=b||{};h.byPassKeys=[9,16,17,18,36,37,38,39,40,91];h.translation={0:{pattern:/\d/},9:{pattern:/\d/,optional:!0},"#":{pattern:/\d/,recursive:!0},A:{pattern:/[a-zA-Z0-9]/}, | |
10 | +S:{pattern:/[a-zA-Z]/}};h.translation=f.extend({},h.translation,b.translation);h=f.extend(!0,{},h,b);p=c.getRegexMask();!1!==b.maxlength&&a.attr("maxlength",d.length);b.placeholder&&a.attr("placeholder",b.placeholder);a.attr("autocomplete","off");c.destroyEvents();c.events();var e=c.getCaret();c.val(c.getMasked());c.setCaret(e+c.getMCharsBeforeCount(e,!0))}()},y={},z=function(){var a=f(this),d={};a.attr("data-mask-reverse")&&(d.reverse=!0);"false"===a.attr("data-mask-maxlength")&&(d.maxlength=!1); | |
11 | +a.attr("data-mask-clearifnotmatch")&&(d.clearIfNotMatch=!0);a.mask(a.attr("data-mask"),d)};f.fn.mask=function(a,d){var b=this.selector,h=function(){var b=f(this).data("mask"),h=JSON.stringify;if("object"!==typeof b||h(b.options)!==h(d)||b.mask!==a)return f(this).data("mask",new A(this,a,d))};this.each(h);b&&!y[b]&&(y[b]=!0,setTimeout(function(){f(document).on("DOMNodeInserted.mask",b,h)},500))};f.fn.unmask=function(){try{return this.each(function(){f(this).data("mask").remove().removeData("mask")})}catch(a){}}; | |
12 | +f.fn.cleanVal=function(){return this.data("mask").getCleanVal()};f("*[data-mask]").each(z);f(document).on("DOMNodeInserted.mask","*[data-mask]",z)}); | ... | ... |
pf/listatelefonica/resources/pf.listatelefonica.masks.js
0 → 100644
1 | +++ a/pf/listatelefonica/resources/pf.listatelefonica.masks.js | |
... | ... | @@ -0,0 +1,23 @@ |
1 | +$(document).ready(function(){ | |
2 | + | |
3 | + var maskBehavior = function (val) { | |
4 | + return val.replace(/\D/g, '').length === 11 ? '(00) 00000-0000' : '(00) 0000-00009'; | |
5 | + }, | |
6 | + options = { | |
7 | + onKeyPress: function(val, e, field, options) { | |
8 | + field.mask(maskBehavior.apply({}, arguments), options); | |
9 | + } | |
10 | + }; | |
11 | + | |
12 | + | |
13 | + $('#form-widgets-ramal1').mask('(00) 0000-0000'); | |
14 | + $('#form-widgets-ramal2').mask('(00) 0000-0000'); | |
15 | + $('#form-widgets-celular').mask(maskBehavior, options); | |
16 | + $('#form-widgets-voip').mask('Z 0000 0000', { | |
17 | + translation: { | |
18 | + 'Z': { | |
19 | + pattern: /(4)/, optional: true | |
20 | + } | |
21 | + } | |
22 | + }); | |
23 | +}); | ... | ... |
1 | +++ a/pf/listatelefonica/resources/unidades.css | |
... | ... | @@ -0,0 +1,64 @@ |
1 | +.lista-person { | |
2 | + margin-bottom: 3em; | |
3 | + font-family: "open_sansregular",Arial,Helvetica,sans-serif; | |
4 | +} | |
5 | + | |
6 | +#unidade-iframe .azul .outstanding-header .outstanding-title { | |
7 | + font-weight: normal !important; | |
8 | + font-family: "open_sansregular",Arial,Helvetica,sans-serif; | |
9 | +} | |
10 | + | |
11 | +#unidade-iframe .outstanding-header .outstanding-title { | |
12 | + font-size: 1.7em !important; | |
13 | +} | |
14 | + | |
15 | +#unidade-iframe table.listing { | |
16 | + border-collapse: collapse !important; | |
17 | + width:100%; | |
18 | +} | |
19 | + | |
20 | +#unidade-iframe table.listing tbody th { | |
21 | + color: #666; | |
22 | + font-weight: bold; | |
23 | +} | |
24 | + | |
25 | +#unidade-iframe table.listing tr.odd th, table.listing tr.even th { | |
26 | + /* font-size: 1.5em!important;*/ | |
27 | + padding: 0.5em 1em; | |
28 | + vertical-align: top; | |
29 | +} | |
30 | + | |
31 | +#unidade-iframe table.listing tbody tr { | |
32 | + text-align: left; | |
33 | +} | |
34 | + | |
35 | +#unidade-iframe table.listing td { | |
36 | + border-right: 1px solid #ddd; | |
37 | + /* font-size: 1.2em;*/ | |
38 | +} | |
39 | + | |
40 | +#unidade-iframe table.invisible td, table.invisible th, table.plain td, table.plain th, table.listing td, table.listing th { | |
41 | + padding: 0.5em 1em; | |
42 | + vertical-align: top; | |
43 | +} | |
44 | + | |
45 | +#unidade-iframe table.listing a { | |
46 | + -moz-border-bottom-colors: none; | |
47 | + -moz-border-left-colors: none; | |
48 | + -moz-border-right-colors: none; | |
49 | + -moz-border-top-colors: none; | |
50 | + border-bottom: medium none !important; | |
51 | + border-image: none; | |
52 | + border-left: medium none; | |
53 | + border-right: medium none; | |
54 | + border-top: medium none; | |
55 | + display: inline-block; | |
56 | + text-decoration: none; | |
57 | +} | |
58 | + | |
59 | +#unidade-iframe div.position-0 { | |
60 | + margin-left:0 ; | |
61 | +} | |
62 | + | |
63 | + | |
64 | + | ... | ... |
1 | +++ a/pf/listatelefonica/setuphandlers.py | |
... | ... | @@ -0,0 +1,120 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | + | |
3 | +from Products.CMFCore.utils import getToolByName | |
4 | + | |
5 | +from plone import api | |
6 | +from plone.app.textfield.value import RichTextValue | |
7 | + | |
8 | + | |
9 | +class Empty(object): | |
10 | + """ """ | |
11 | + | |
12 | + | |
13 | +def install_index(context): | |
14 | + portal = context.getSite() | |
15 | + | |
16 | + # ADIÇÃO DOS INTEX DE CATALOG | |
17 | + catalog = getToolByName(portal, 'portal_catalog') | |
18 | + indexes = catalog.indexes() | |
19 | + # Specify the indexes you want, with ('index_name', 'index_type') | |
20 | + wanted = ( | |
21 | + ('nome', 'ZCTextIndex'), | |
22 | + ('ramal1', 'ZCTextIndex'), | |
23 | + ('ramal2', 'ZCTextIndex'), | |
24 | + ('celular', 'ZCTextIndex'), | |
25 | + ('voip', 'ZCTextIndex'), | |
26 | + ('unidade', 'ZCTextIndex'), | |
27 | + ('cargo', 'ZCTextIndex'), | |
28 | + ('funcao', 'ZCTextIndex'), | |
29 | + ) | |
30 | + indexables = [] | |
31 | + for name, meta_type in wanted: | |
32 | + if name not in indexes: | |
33 | + # Required for ZCTextIndex indexes | |
34 | + title_extras = Empty() | |
35 | + title_extras.index_type = 'Okapi BM25 Rank' | |
36 | + title_extras.lexicon_id = 'plone_lexicon' | |
37 | + | |
38 | + # Add indexes on catalog | |
39 | + catalog.addIndex(name, meta_type, title_extras) | |
40 | + indexables.append(name) | |
41 | + | |
42 | + if len(indexables) > 0: | |
43 | + catalog.manage_reindexIndex(ids=indexables) | |
44 | + | |
45 | + | |
46 | +def container_has_content(container, content_id): | |
47 | + try: | |
48 | + container[content_id] | |
49 | + return True | |
50 | + except KeyError: | |
51 | + return False | |
52 | + | |
53 | + | |
54 | +def get_or_create_content(container, type, id, title, **kwargs): | |
55 | + try: | |
56 | + content = container[id] | |
57 | + except KeyError: | |
58 | + content = api.content.create( | |
59 | + container=container, | |
60 | + type=type, | |
61 | + id=id, | |
62 | + title=title, | |
63 | + **kwargs) | |
64 | + return content | |
65 | + | |
66 | + | |
67 | +def create_initial_content(context): | |
68 | + portal = api.portal.get() | |
69 | + | |
70 | + # Biblioteca de Servicos page | |
71 | + lista = get_or_create_content( | |
72 | + container=portal, | |
73 | + type='Folder', | |
74 | + id='lista-telefonica', | |
75 | + title='Lista Telefônica', | |
76 | + description='Esse serviço está temporariamente indisponível' | |
77 | + ) | |
78 | + lista.setLayout('listatelefonica_view') | |
79 | + lista.manage_setLocalRoles("AuthenticatedUsers", ["Contributor", ]) | |
80 | + | |
81 | + # Biblioteca de Servicos description page | |
82 | + if not container_has_content(lista, 'lista-telefonica'): | |
83 | + description_text = u"""\ | |
84 | + <p>Descrição do serviço de Lista Telefônica </p> | |
85 | + """ | |
86 | + obj_desc = get_or_create_content( | |
87 | + container=lista, | |
88 | + type='Document', | |
89 | + id='lista-telefonica', | |
90 | + title='Descrição do serviço', | |
91 | + text=RichTextValue( | |
92 | + description_text, | |
93 | + 'text/html', | |
94 | + 'text/plain' | |
95 | + ) | |
96 | + ) | |
97 | + if api.content.get_state(obj=obj_desc) != 'published': | |
98 | + api.content.transition(obj=obj_desc, transition='publish') | |
99 | + | |
100 | + if api.content.get_state(obj=lista) != 'published': | |
101 | + api.content.transition(obj=lista, transition='publish') | |
102 | + | |
103 | + # Folder Serviços do Portal: used to include links to portal services | |
104 | + sp = get_or_create_content( | |
105 | + container=portal, | |
106 | + type='Folder', | |
107 | + id='servicos-do-portal', | |
108 | + title='Serviços do Portal', | |
109 | + exclude_from_nav=True, | |
110 | + ) | |
111 | + if api.content.get_state(obj=sp) != 'published': | |
112 | + api.content.transition(obj=sp, transition='publish') | |
113 | + | |
114 | + get_or_create_content( | |
115 | + container=sp, | |
116 | + type='Link', | |
117 | + id='lista-telefonica', | |
118 | + title='Lista Telefônica', | |
119 | + remoteUrl='${portal_url}/lista-telefonica' | |
120 | + ) | ... | ... |
1 | +++ a/pf/listatelefonica/tests.py | |
... | ... | @@ -0,0 +1,55 @@ |
1 | +import unittest | |
2 | + | |
3 | +#from zope.testing import doctestunit | |
4 | +#from zope.component import testing | |
5 | +from Testing import ZopeTestCase as ztc | |
6 | + | |
7 | +from Products.Five import fiveconfigure | |
8 | +from Products.PloneTestCase import PloneTestCase as ptc | |
9 | +from Products.PloneTestCase.layer import PloneSite | |
10 | +ptc.setupPloneSite() | |
11 | + | |
12 | +import pf.listatelefonica | |
13 | + | |
14 | + | |
15 | +class TestCase(ptc.PloneTestCase): | |
16 | + | |
17 | + class layer(PloneSite): | |
18 | + | |
19 | + @classmethod | |
20 | + def setUp(cls): | |
21 | + fiveconfigure.debug_mode = True | |
22 | + ztc.installPackage(pf.listatelefonica) | |
23 | + fiveconfigure.debug_mode = False | |
24 | + | |
25 | + @classmethod | |
26 | + def tearDown(cls): | |
27 | + pass | |
28 | + | |
29 | + | |
30 | +def test_suite(): | |
31 | + return unittest.TestSuite([ | |
32 | + | |
33 | + # Unit tests | |
34 | + #doctestunit.DocFileSuite( | |
35 | + # 'README.txt', package='pf.listatelefonica', | |
36 | + # setUp=testing.setUp, tearDown=testing.tearDown), | |
37 | + | |
38 | + #doctestunit.DocTestSuite( | |
39 | + # module='pf.listatelefonica.mymodule', | |
40 | + # setUp=testing.setUp, tearDown=testing.tearDown), | |
41 | + | |
42 | + | |
43 | + # Integration tests that use PloneTestCase | |
44 | + #ztc.ZopeDocFileSuite( | |
45 | + # 'README.txt', package='pf.listatelefonica', | |
46 | + # test_class=TestCase), | |
47 | + | |
48 | + #ztc.FunctionalDocFileSuite( | |
49 | + # 'browser.txt', package='pf.listatelefonica', | |
50 | + # test_class=TestCase), | |
51 | + | |
52 | + ]) | |
53 | + | |
54 | +if __name__ == '__main__': | |
55 | + unittest.main(defaultTest='test_suite') | ... | ... |
1 | +++ a/setup.py | |
... | ... | @@ -0,0 +1,49 @@ |
1 | +# -*- coding: utf-8 -*- | |
2 | + | |
3 | +from setuptools import setup, find_packages | |
4 | +import os | |
5 | + | |
6 | +version = '1.1.2' | |
7 | + | |
8 | +setup(name='pf.listatelefonica', | |
9 | + version=version, | |
10 | + description="Projeto de Lista Telefonica da Policia Federal", | |
11 | + long_description=open("README.txt").read() + "\n" + | |
12 | + open(os.path.join("docs", "HISTORY.txt")).read(), | |
13 | + # Get more strings from | |
14 | + # http://pypi.python.org/pypi?:action=list_classifiers | |
15 | + classifiers=[ | |
16 | + "Framework :: Plone", | |
17 | + "Programming Language :: Python", | |
18 | + ], | |
19 | + keywords='', | |
20 | + author='Liberiun', | |
21 | + author_email='', | |
22 | + url='https://svn.dpf.gov.br/sds/portal_internet/2-Implementacao/trunk/produtos/pf.listatelefonica/', | |
23 | + license='Creative Commons General Public License - GPL (“Licença Pública Geral”), versão 2.0, em português', | |
24 | + packages=find_packages(exclude=['ez_setup']), | |
25 | + namespace_packages=['pf'], | |
26 | + include_package_data=True, | |
27 | + zip_safe=False, | |
28 | + install_requires=[ | |
29 | + 'setuptools', | |
30 | + # -*- Extra requirements: -*- | |
31 | + 'plone.api', | |
32 | + 'plone.app.dexterity [grok, relations]', | |
33 | + 'plone.app.relationfield', | |
34 | + 'plone.namedfile [blobs]', | |
35 | + 'plone.formwidget.namedfile', | |
36 | + 'plone.behavior', | |
37 | + 'collective.dexteritytextindexer', | |
38 | + 'Products.AdvancedQuery', | |
39 | + 'plone.app.ldap', | |
40 | + ], | |
41 | + entry_points=""" | |
42 | + # -*- Entry points: -*- | |
43 | + | |
44 | + [z3c.autoinclude.plugin] | |
45 | + target = plone | |
46 | + """, | |
47 | + # setup_requires=["PasteScript"], | |
48 | + # paster_plugins=["ZopeSkel"], | |
49 | + ) | ... | ... |