Commit a1e9df306f7b6a443517379c00efeb0d033713c0

Authored by Bruno Barbosa
0 parents
Exists in master

initial release

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