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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 @@ | @@ -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 | + ) |