From ad8dc17265a06f2e77cd5c7ae00d7b3e388b8307 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Sun, 26 Jun 2011 10:44:28 +0000 Subject: [PATCH] new PLA release --- .../3rdParty/pla/htdocs/add_attr_form.php | 185 + .../3rdParty/pla/htdocs/add_oclass_form.php | 117 + .../3rdParty/pla/htdocs/add_value_form.php | 173 + lam/templates/3rdParty/pla/htdocs/cmd.php | 83 + .../3rdParty/pla/htdocs/collapse.php | 27 + lam/templates/3rdParty/pla/htdocs/common.php | 14 + lam/templates/3rdParty/pla/htdocs/compare.php | 188 + .../3rdParty/pla/htdocs/compare_form.php | 65 + lam/templates/3rdParty/pla/htdocs/copy.php | 209 ++ .../3rdParty/pla/htdocs/copy_form.php | 103 + lam/templates/3rdParty/pla/htdocs/create.php | 104 + .../3rdParty/pla/htdocs/create_confirm.php | 143 + .../3rdParty/pla/htdocs/css/default/style.css | 915 +++++ lam/templates/3rdParty/pla/htdocs/delete.php | 40 + .../3rdParty/pla/htdocs/delete_attr.php | 49 + .../3rdParty/pla/htdocs/delete_form.php | 157 + .../pla/htdocs/download_binary_attr.php | 49 + .../3rdParty/pla/htdocs/draw_tree_node.php | 61 + .../3rdParty/pla/htdocs/entry_chooser.php | 123 + lam/templates/3rdParty/pla/htdocs/expand.php | 27 + lam/templates/3rdParty/pla/htdocs/export.php | 40 + .../3rdParty/pla/htdocs/export_form.php | 213 ++ .../pla/htdocs/images/ajax-progress.gif | Bin 0 -> 7685 bytes .../pla/htdocs/images/ajax-spinner.gif | Bin 0 -> 2037 bytes .../pla/htdocs/images/countries/af.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/al.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/am.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/an.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/ao.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/ar.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/at.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/au.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/aw.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/az.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/ba.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/bb.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/bd.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/be.png | Bin 0 -> 1039 bytes .../pla/htdocs/images/countries/bf.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/bg.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/bh.png | Bin 0 -> 1031 bytes .../pla/htdocs/images/countries/bi.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/bj.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/bm.png | Bin 0 -> 1036 bytes .../pla/htdocs/images/countries/bn.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/bo.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/br.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/bs.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/bt.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/bw.png | Bin 0 -> 1032 bytes .../pla/htdocs/images/countries/by.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/bz.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/ca.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/cf.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/cg.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/ch.png | Bin 0 -> 1031 bytes .../pla/htdocs/images/countries/ci.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/ck.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/cl.png | Bin 0 -> 253 bytes .../pla/htdocs/images/countries/cm.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/cn.png | Bin 0 -> 624 bytes .../pla/htdocs/images/countries/co.png | Bin 0 -> 1030 bytes .../pla/htdocs/images/countries/cr.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/cu.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/cv.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/cy.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/cz.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/de.png | Bin 0 -> 1038 bytes .../pla/htdocs/images/countries/dk.png | Bin 0 -> 1037 bytes .../pla/htdocs/images/countries/dz.png | Bin 0 -> 1037 bytes .../pla/htdocs/images/countries/ec.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/ee.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/eg.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/er.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/es.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/et.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/fi.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/fj.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/fo.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/fr.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/ga.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/gb.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/ge.png | Bin 0 -> 628 bytes .../pla/htdocs/images/countries/gi.png | Bin 0 -> 1039 bytes .../pla/htdocs/images/countries/gl.png | Bin 0 -> 1038 bytes .../pla/htdocs/images/countries/gp.png | Bin 0 -> 1030 bytes .../pla/htdocs/images/countries/gr.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/gt.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/gu.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/gy.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/hk.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/hr.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/ht.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/hu.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/id.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/ie.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/il.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/in.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/iq.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/ir.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/is.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/it.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/jm.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/jo.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/jp.png | Bin 0 -> 648 bytes .../pla/htdocs/images/countries/ke.png | Bin 0 -> 1039 bytes .../pla/htdocs/images/countries/kg.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/kh.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/ki.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/kp.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/kr.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/ky.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/kz.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/lb.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/lc.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/lk.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/lt.png | Bin 0 -> 646 bytes .../pla/htdocs/images/countries/lu.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/lv.png | Bin 0 -> 1032 bytes .../pla/htdocs/images/countries/ly.png | Bin 0 -> 623 bytes .../pla/htdocs/images/countries/ma.png | Bin 0 -> 1175 bytes .../pla/htdocs/images/countries/mc.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/md.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/mg.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/mn.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/mo.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/mp.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/ms.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/mt.png | Bin 0 -> 873 bytes .../pla/htdocs/images/countries/mx.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/my.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/mz.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/na.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/nc.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/nf.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/nl.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/no.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/np.png | Bin 0 -> 730 bytes .../pla/htdocs/images/countries/nr.png | Bin 0 -> 1038 bytes .../pla/htdocs/images/countries/nz.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/om.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/pa.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/pe.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/pf.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/ph.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/pk.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/pl.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/pm.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/pr.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/pt.png | Bin 0 -> 1036 bytes .../pla/htdocs/images/countries/py.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/qa.png | Bin 0 -> 647 bytes .../pla/htdocs/images/countries/ro.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/ru.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/sa.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/sb.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/sd.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/se.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/sg.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/si.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/sk.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/sl.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/so.png | Bin 0 -> 1033 bytes .../pla/htdocs/images/countries/sr.png | Bin 0 -> 899 bytes .../pla/htdocs/images/countries/sy.png | Bin 0 -> 920 bytes .../pla/htdocs/images/countries/tc.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/tg.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/th.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/tn.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/to.png | Bin 0 -> 649 bytes .../pla/htdocs/images/countries/tp.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/tr.png | Bin 0 -> 1029 bytes .../pla/htdocs/images/countries/tt.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/tv.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/tw.png | Bin 0 -> 633 bytes .../pla/htdocs/images/countries/tz.png | Bin 0 -> 1040 bytes .../pla/htdocs/images/countries/ua.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/ug.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/us.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/uy.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/va.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/ve.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/vg.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/vi.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/vn.png | Bin 0 -> 1038 bytes .../pla/htdocs/images/countries/ws.png | Bin 0 -> 1027 bytes .../pla/htdocs/images/countries/ye.png | Bin 0 -> 1041 bytes .../pla/htdocs/images/countries/yu.png | Bin 0 -> 1042 bytes .../pla/htdocs/images/countries/za.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/countries/zw.png | Bin 0 -> 1043 bytes .../pla/htdocs/images/default/add.png | Bin 0 -> 612 bytes .../htdocs/images/default/address-book.png | Bin 0 -> 1302 bytes .../pla/htdocs/images/default/calendar.png | Bin 0 -> 478 bytes .../pla/htdocs/images/default/catalog.png | Bin 0 -> 1302 bytes .../pla/htdocs/images/default/children.png | Bin 0 -> 342 bytes .../pla/htdocs/images/default/compare.png | Bin 0 -> 662 bytes .../pla/htdocs/images/default/country.png | Bin 0 -> 707 bytes .../pla/htdocs/images/default/create.png | Bin 0 -> 700 bytes .../pla/htdocs/images/default/cut.png | Bin 0 -> 340 bytes .../pla/htdocs/images/default/delete.gif | Bin 0 -> 624 bytes .../pla/htdocs/images/default/device.png | Bin 0 -> 500 bytes .../pla/htdocs/images/default/disabled.png | Bin 0 -> 692 bytes .../pla/htdocs/images/default/document.png | Bin 0 -> 856 bytes .../pla/htdocs/images/default/door.png | Bin 0 -> 364 bytes .../pla/htdocs/images/default/error-big.png | Bin 0 -> 1891 bytes .../pla/htdocs/images/default/error.png | Bin 0 -> 796 bytes .../pla/htdocs/images/default/export-big.png | Bin 0 -> 838 bytes .../pla/htdocs/images/default/export.png | Bin 0 -> 838 bytes .../pla/htdocs/images/default/files.png | Bin 0 -> 1165 bytes .../pla/htdocs/images/default/find.png | Bin 0 -> 1428 bytes .../pla/htdocs/images/default/folder.png | Bin 0 -> 1217 bytes .../pla/htdocs/images/default/forum-big.png | Bin 0 -> 738 bytes .../pla/htdocs/images/default/hard-drive.png | Bin 0 -> 1308 bytes .../pla/htdocs/images/default/help-big.png | Bin 0 -> 1615 bytes .../pla/htdocs/images/default/help.png | Bin 0 -> 738 bytes .../pla/htdocs/images/default/home-big.png | Bin 0 -> 1084 bytes .../pla/htdocs/images/default/home.png | Bin 0 -> 1105 bytes .../pla/htdocs/images/default/host.png | Bin 0 -> 844 bytes .../pla/htdocs/images/default/import-big.png | Bin 0 -> 749 bytes .../pla/htdocs/images/default/info-big.png | Bin 0 -> 966 bytes .../pla/htdocs/images/default/invalid.png | Bin 0 -> 692 bytes .../pla/htdocs/images/default/key.png | Bin 0 -> 519 bytes .../pla/htdocs/images/default/ldap-alias.png | Bin 0 -> 440 bytes .../pla/htdocs/images/default/ldap-dc.png | Bin 0 -> 1140 bytes .../htdocs/images/default/ldap-default.png | Bin 0 -> 1297 bytes .../pla/htdocs/images/default/ldap-o.png | Bin 0 -> 918 bytes .../pla/htdocs/images/default/ldap-ou.png | Bin 0 -> 1330 bytes .../pla/htdocs/images/default/ldap-server.png | Bin 0 -> 1084 bytes .../pla/htdocs/images/default/ldap-uid.png | Bin 0 -> 654 bytes .../images/default/ldap-uniquegroup.png | Bin 0 -> 1356 bytes .../pla/htdocs/images/default/ldap-user.png | Bin 0 -> 773 bytes .../pla/htdocs/images/default/light-big.png | Bin 0 -> 1950 bytes .../pla/htdocs/images/default/light.png | Bin 0 -> 733 bytes .../pla/htdocs/images/default/locality.png | Bin 0 -> 751 bytes .../pla/htdocs/images/default/lock.png | Bin 0 -> 1256 bytes .../pla/htdocs/images/default/mail.png | Bin 0 -> 899 bytes .../htdocs/images/default/mail_account.png | Bin 0 -> 993 bytes .../pla/htdocs/images/default/mail_alias.png | Bin 0 -> 938 bytes .../pla/htdocs/images/default/minus.png | Bin 0 -> 98 bytes .../images/default/monitorserver-big.png | Bin 0 -> 1084 bytes .../pla/htdocs/images/default/move.png | Bin 0 -> 483 bytes .../3rdParty/pla/htdocs/images/default/n.png | Bin 0 -> 408 bytes .../pla/htdocs/images/default/network.png | Bin 0 -> 1107 bytes .../pla/htdocs/images/default/nogo.png | Bin 0 -> 243 bytes .../pla/htdocs/images/default/nt_machine.png | Bin 0 -> 1060 bytes .../pla/htdocs/images/default/nt_user.png | Bin 0 -> 971 bytes .../pla/htdocs/images/default/phone.png | Bin 0 -> 878 bytes .../pla/htdocs/images/default/photo.png | Bin 0 -> 572 bytes .../pla/htdocs/images/default/plus.png | Bin 0 -> 102 bytes .../pla/htdocs/images/default/process.png | Bin 0 -> 438 bytes .../pla/htdocs/images/default/refresh-big.png | Bin 0 -> 463 bytes .../pla/htdocs/images/default/refresh.png | Bin 0 -> 463 bytes .../pla/htdocs/images/default/rename.png | Bin 0 -> 418 bytes .../pla/htdocs/images/default/rfc.png | Bin 0 -> 637 bytes .../pla/htdocs/images/default/save.png | Bin 0 -> 838 bytes .../pla/htdocs/images/default/search-big.png | Bin 0 -> 815 bytes .../htdocs/images/default/server-settings.png | Bin 0 -> 1149 bytes .../htdocs/images/default/server-small.png | Bin 0 -> 818 bytes .../pla/htdocs/images/default/server.png | Bin 0 -> 1424 bytes .../pla/htdocs/images/default/switch.png | Bin 0 -> 1302 bytes .../pla/htdocs/images/default/terminal.png | Bin 0 -> 851 bytes .../pla/htdocs/images/default/tools-no.png | Bin 0 -> 798 bytes .../pla/htdocs/images/default/tools.png | Bin 0 -> 503 bytes .../pla/htdocs/images/default/trash.png | Bin 0 -> 708 bytes .../htdocs/images/default/tree_collapse.png | Bin 0 -> 196 bytes .../images/default/tree_collapse_corner.png | Bin 0 -> 196 bytes .../default/tree_collapse_corner_first.png | Bin 0 -> 189 bytes .../images/default/tree_collapse_first.png | Bin 0 -> 194 bytes .../pla/htdocs/images/default/tree_corner.png | Bin 0 -> 160 bytes .../pla/htdocs/images/default/tree_expand.png | Bin 0 -> 204 bytes .../images/default/tree_expand_corner.png | Bin 0 -> 204 bytes .../default/tree_expand_corner_first.png | Bin 0 -> 197 bytes .../images/default/tree_expand_first.png | Bin 0 -> 202 bytes .../images/default/tree_folder_closed.png | Bin 0 -> 247 bytes .../images/default/tree_folder_open.png | Bin 0 -> 278 bytes .../pla/htdocs/images/default/tree_leaf.png | Bin 0 -> 207 bytes .../pla/htdocs/images/default/tree_space.png | Bin 0 -> 150 bytes .../pla/htdocs/images/default/tree_split.png | Bin 0 -> 160 bytes .../images/default/tree_split_first.png | Bin 0 -> 160 bytes .../htdocs/images/default/tree_vertline.png | Bin 0 -> 156 bytes .../pla/htdocs/images/default/unknown.png | Bin 0 -> 924 bytes .../3rdParty/pla/htdocs/images/default/up.png | Bin 0 -> 382 bytes .../pla/htdocs/images/default/warn-big.png | Bin 0 -> 2293 bytes .../3rdParty/pla/htdocs/images/favicon.ico | Bin 0 -> 4286 bytes lam/templates/3rdParty/pla/htdocs/import.php | 111 + .../3rdParty/pla/htdocs/import_form.php | 48 + lam/templates/3rdParty/pla/htdocs/index.php | 147 + .../3rdParty/pla/htdocs/mass_delete.php | 147 + .../3rdParty/pla/htdocs/mass_edit.php | 133 + .../3rdParty/pla/htdocs/mass_update.php | 175 + .../pla/htdocs/modify_member_form.php | 178 + .../3rdParty/pla/htdocs/password_checker.php | 88 + .../3rdParty/pla/htdocs/query_engine.php | 19 + lam/templates/3rdParty/pla/htdocs/rdelete.php | 89 + lam/templates/3rdParty/pla/htdocs/refresh.php | 46 + lam/templates/3rdParty/pla/htdocs/rename.php | 64 + .../3rdParty/pla/htdocs/rename_form.php | 40 + .../3rdParty/pla/htdocs/template_engine.php | 57 + lam/templates/3rdParty/pla/htdocs/update.php | 78 + .../3rdParty/pla/htdocs/update_confirm.php | 253 ++ .../3rdParty/pla/htdocs/view_jpeg_photo.php | 77 + lam/templates/3rdParty/pla/index.php | 11 + lam/templates/3rdParty/pla/lib/AJAXTree.php | 307 ++ lam/templates/3rdParty/pla/lib/Attribute.php | 917 +++++ .../3rdParty/pla/lib/AttributeFactory.php | 188 + .../3rdParty/pla/lib/BinaryAttribute.php | 60 + .../3rdParty/pla/lib/DateAttribute.php | 17 + .../3rdParty/pla/lib/DnAttribute.php | 17 + .../3rdParty/pla/lib/GidAttribute.php | 17 + lam/templates/3rdParty/pla/lib/HTMLTree.php | 588 ++++ .../3rdParty/pla/lib/JpegAttribute.php | 17 + lam/templates/3rdParty/pla/lib/MassRender.php | 48 + .../3rdParty/pla/lib/MultiLineAttribute.php | 35 + .../3rdParty/pla/lib/ObjectClassAttribute.php | 17 + lam/templates/3rdParty/pla/lib/PageRender.php | 1225 +++++++ .../3rdParty/pla/lib/PasswordAttribute.php | 17 + lam/templates/3rdParty/pla/lib/Query.php | 283 ++ .../3rdParty/pla/lib/QueryRender.php | 552 +++ .../pla/lib/RandomPasswordAttribute.php | 17 + .../pla/lib/SambaPasswordAttribute.php | 17 + .../3rdParty/pla/lib/SelectionAttribute.php | 71 + .../3rdParty/pla/lib/ShadowAttribute.php | 19 + lam/templates/3rdParty/pla/lib/Template.php | 1567 +++++++++ .../3rdParty/pla/lib/TemplateRender.php | 2515 ++++++++++++++ lam/templates/3rdParty/pla/lib/Tree.php | 344 ++ lam/templates/3rdParty/pla/lib/TreeItem.php | 312 ++ lam/templates/3rdParty/pla/lib/Visitor.php | 112 + lam/templates/3rdParty/pla/lib/blowfish.php | 480 +++ lam/templates/3rdParty/pla/lib/common.php | 266 ++ .../3rdParty/pla/lib/config_default.php | 803 +++++ lam/templates/3rdParty/pla/lib/createlm.php | 381 ++ lam/templates/3rdParty/pla/lib/ds.php | 726 ++++ .../3rdParty/pla/lib/ds_ldap_pla.php | 653 ++++ lam/templates/3rdParty/pla/lib/ds_myldap.php | 2334 +++++++++++++ .../3rdParty/pla/lib/emuhash_functions.php | 112 + .../3rdParty/pla/lib/export_functions.php | 641 ++++ lam/templates/3rdParty/pla/lib/functions.php | 3076 +++++++++++++++++ lam/templates/3rdParty/pla/lib/hooks.php | 201 ++ .../3rdParty/pla/lib/import_functions.php | 609 ++++ .../3rdParty/pla/lib/ldap_error_codes.txt | 94 + .../3rdParty/pla/lib/ldap_supported_oids.txt | 187 + lam/templates/3rdParty/pla/lib/page.php | 510 +++ .../3rdParty/pla/lib/query_functions.php | 22 + .../3rdParty/pla/lib/schema_functions.php | 1539 +++++++++ .../3rdParty/pla/lib/session_functions.php | 31 + lam/templates/3rdParty/pla/lib/syslog.php | 88 + .../3rdParty/pla/lib/template_functions.php | 23 + lam/templates/3rdParty/pla/lib/xml2array.php | 144 + .../3rdParty/pla/lib/xmlTemplates.php | 495 +++ .../3rdParty/pla/templates/creation/ou.xml | 27 + .../pla/templates/modification/.placeholder | 1 + .../3rdParty/pla/tools/unserialize.php | 26 + 352 files changed, 27594 insertions(+) create mode 100644 lam/templates/3rdParty/pla/htdocs/add_attr_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/add_oclass_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/add_value_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/cmd.php create mode 100644 lam/templates/3rdParty/pla/htdocs/collapse.php create mode 100644 lam/templates/3rdParty/pla/htdocs/common.php create mode 100644 lam/templates/3rdParty/pla/htdocs/compare.php create mode 100644 lam/templates/3rdParty/pla/htdocs/compare_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/copy.php create mode 100644 lam/templates/3rdParty/pla/htdocs/copy_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/create.php create mode 100644 lam/templates/3rdParty/pla/htdocs/create_confirm.php create mode 100644 lam/templates/3rdParty/pla/htdocs/css/default/style.css create mode 100644 lam/templates/3rdParty/pla/htdocs/delete.php create mode 100644 lam/templates/3rdParty/pla/htdocs/delete_attr.php create mode 100644 lam/templates/3rdParty/pla/htdocs/delete_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/download_binary_attr.php create mode 100644 lam/templates/3rdParty/pla/htdocs/draw_tree_node.php create mode 100644 lam/templates/3rdParty/pla/htdocs/entry_chooser.php create mode 100644 lam/templates/3rdParty/pla/htdocs/expand.php create mode 100644 lam/templates/3rdParty/pla/htdocs/export.php create mode 100644 lam/templates/3rdParty/pla/htdocs/export_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/images/ajax-progress.gif create mode 100644 lam/templates/3rdParty/pla/htdocs/images/ajax-spinner.gif create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/af.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/al.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/am.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/an.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ao.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ar.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/at.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/au.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/aw.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/az.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ba.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bb.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bd.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/be.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bf.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bh.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bi.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bj.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bm.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bn.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bo.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/br.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bs.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bt.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bw.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/by.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/bz.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ca.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cf.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ch.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ci.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ck.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cl.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cm.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cn.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/co.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cu.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cv.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cy.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/cz.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/de.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/dk.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/dz.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ec.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ee.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/eg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/er.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/es.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/et.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/fi.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/fj.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/fo.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/fr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ga.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gb.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ge.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gi.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gl.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gp.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gt.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gu.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/gy.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/hk.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/hr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ht.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/hu.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/id.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ie.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/il.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/in.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/iq.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ir.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/is.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/it.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/jm.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/jo.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/jp.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ke.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/kg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/kh.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ki.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/kp.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/kr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ky.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/kz.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/lb.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/lc.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/lk.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/lt.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/lu.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/lv.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ly.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ma.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mc.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/md.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mn.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mo.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mp.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ms.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mt.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mx.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/my.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/mz.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/na.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/nc.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/nf.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/nl.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/no.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/np.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/nr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/nz.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/om.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pa.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pe.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pf.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ph.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pk.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pl.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pm.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/pt.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/py.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/qa.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ro.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ru.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sa.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sb.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sd.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/se.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/si.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sk.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sl.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/so.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/sy.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tc.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/th.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tn.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/to.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tp.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tr.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tt.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tv.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tw.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/tz.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ua.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ug.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/us.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/uy.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/va.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ve.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/vg.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/vi.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/vn.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ws.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/ye.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/yu.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/za.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/countries/zw.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/add.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/address-book.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/calendar.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/catalog.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/children.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/compare.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/country.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/create.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/cut.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/delete.gif create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/device.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/disabled.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/document.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/door.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/error-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/error.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/export-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/export.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/files.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/find.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/folder.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/forum-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/hard-drive.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/help-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/help.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/home-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/home.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/host.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/import-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/info-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/invalid.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/key.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-alias.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-dc.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-default.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-o.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-ou.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-server.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-uid.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-uniquegroup.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/ldap-user.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/light-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/light.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/locality.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/lock.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/mail.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/mail_account.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/mail_alias.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/minus.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/monitorserver-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/move.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/n.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/network.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/nogo.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/nt_machine.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/nt_user.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/phone.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/photo.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/plus.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/process.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/refresh-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/refresh.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/rename.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/rfc.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/save.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/search-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/server-settings.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/server-small.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/server.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/switch.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/terminal.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tools-no.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tools.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/trash.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse_corner.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse_corner_first.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse_first.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_corner.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_expand.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_expand_corner.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_expand_corner_first.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_expand_first.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_folder_closed.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_folder_open.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_leaf.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_space.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_split.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_split_first.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/tree_vertline.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/unknown.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/up.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/default/warn-big.png create mode 100644 lam/templates/3rdParty/pla/htdocs/images/favicon.ico create mode 100644 lam/templates/3rdParty/pla/htdocs/import.php create mode 100644 lam/templates/3rdParty/pla/htdocs/import_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/index.php create mode 100644 lam/templates/3rdParty/pla/htdocs/mass_delete.php create mode 100644 lam/templates/3rdParty/pla/htdocs/mass_edit.php create mode 100644 lam/templates/3rdParty/pla/htdocs/mass_update.php create mode 100644 lam/templates/3rdParty/pla/htdocs/modify_member_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/password_checker.php create mode 100644 lam/templates/3rdParty/pla/htdocs/query_engine.php create mode 100644 lam/templates/3rdParty/pla/htdocs/rdelete.php create mode 100644 lam/templates/3rdParty/pla/htdocs/refresh.php create mode 100644 lam/templates/3rdParty/pla/htdocs/rename.php create mode 100644 lam/templates/3rdParty/pla/htdocs/rename_form.php create mode 100644 lam/templates/3rdParty/pla/htdocs/template_engine.php create mode 100644 lam/templates/3rdParty/pla/htdocs/update.php create mode 100644 lam/templates/3rdParty/pla/htdocs/update_confirm.php create mode 100644 lam/templates/3rdParty/pla/htdocs/view_jpeg_photo.php create mode 100644 lam/templates/3rdParty/pla/index.php create mode 100644 lam/templates/3rdParty/pla/lib/AJAXTree.php create mode 100644 lam/templates/3rdParty/pla/lib/Attribute.php create mode 100644 lam/templates/3rdParty/pla/lib/AttributeFactory.php create mode 100644 lam/templates/3rdParty/pla/lib/BinaryAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/DateAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/DnAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/GidAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/HTMLTree.php create mode 100644 lam/templates/3rdParty/pla/lib/JpegAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/MassRender.php create mode 100644 lam/templates/3rdParty/pla/lib/MultiLineAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/ObjectClassAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/PageRender.php create mode 100644 lam/templates/3rdParty/pla/lib/PasswordAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/Query.php create mode 100644 lam/templates/3rdParty/pla/lib/QueryRender.php create mode 100644 lam/templates/3rdParty/pla/lib/RandomPasswordAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/SambaPasswordAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/SelectionAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/ShadowAttribute.php create mode 100644 lam/templates/3rdParty/pla/lib/Template.php create mode 100644 lam/templates/3rdParty/pla/lib/TemplateRender.php create mode 100644 lam/templates/3rdParty/pla/lib/Tree.php create mode 100644 lam/templates/3rdParty/pla/lib/TreeItem.php create mode 100644 lam/templates/3rdParty/pla/lib/Visitor.php create mode 100644 lam/templates/3rdParty/pla/lib/blowfish.php create mode 100644 lam/templates/3rdParty/pla/lib/common.php create mode 100644 lam/templates/3rdParty/pla/lib/config_default.php create mode 100644 lam/templates/3rdParty/pla/lib/createlm.php create mode 100644 lam/templates/3rdParty/pla/lib/ds.php create mode 100644 lam/templates/3rdParty/pla/lib/ds_ldap_pla.php create mode 100644 lam/templates/3rdParty/pla/lib/ds_myldap.php create mode 100644 lam/templates/3rdParty/pla/lib/emuhash_functions.php create mode 100644 lam/templates/3rdParty/pla/lib/export_functions.php create mode 100644 lam/templates/3rdParty/pla/lib/functions.php create mode 100644 lam/templates/3rdParty/pla/lib/hooks.php create mode 100644 lam/templates/3rdParty/pla/lib/import_functions.php create mode 100644 lam/templates/3rdParty/pla/lib/ldap_error_codes.txt create mode 100644 lam/templates/3rdParty/pla/lib/ldap_supported_oids.txt create mode 100644 lam/templates/3rdParty/pla/lib/page.php create mode 100644 lam/templates/3rdParty/pla/lib/query_functions.php create mode 100644 lam/templates/3rdParty/pla/lib/schema_functions.php create mode 100644 lam/templates/3rdParty/pla/lib/session_functions.php create mode 100644 lam/templates/3rdParty/pla/lib/syslog.php create mode 100644 lam/templates/3rdParty/pla/lib/template_functions.php create mode 100644 lam/templates/3rdParty/pla/lib/xml2array.php create mode 100644 lam/templates/3rdParty/pla/lib/xmlTemplates.php create mode 100644 lam/templates/3rdParty/pla/templates/creation/ou.xml create mode 100644 lam/templates/3rdParty/pla/templates/modification/.placeholder create mode 100644 lam/templates/3rdParty/pla/tools/unserialize.php diff --git a/lam/templates/3rdParty/pla/htdocs/add_attr_form.php b/lam/templates/3rdParty/pla/htdocs/add_attr_form.php new file mode 100644 index 00000000..7f728413 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/add_attr_form.php @@ -0,0 +1,185 @@ +dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$request['page'] = new TemplateRender($app['server']->getIndex(),get_request('template','REQUEST',false,null)); +$request['page']->setDN($request['dn']); +$request['page']->accept(true); +$request['template'] = $request['page']->getTemplate(); + +# Render the form +if (get_request('meth','REQUEST') != 'ajax') { + $request['page']->drawTitle(sprintf('%s %s',_('Add new attribute'),get_rdn($request['dn']))); + $request['page']->drawSubTitle(); + + echo '
'; + if (count($request['template']->getAvailAttrs())) { + # If we have more than the configured entries, we'll separate our input to the old ways. + if (count($request['template']->getAvailAttrs()) > $_SESSION[APPCONFIG]->getValue('appearance','max_add_attrs')) { + $attr = array(); + $attr['avail'] = array(); + $attr['binary'] = array(); + + foreach ($request['template']->getAvailAttrs() as $attribute) + if ($app['server']->isAttrBinary($attribute->getName())) + array_push($attr['binary'],$attribute); + else + array_push($attr['avail'],$attribute); + + if (count($attr['avail']) > 0) { + echo '
'; + echo _('Add new attribute'); + echo '
'; + echo '
'; + + echo '
'; + echo '
'; + + if ($_SESSION[APPCONFIG]->getValue('confirm','update')) + echo ''; + else + echo ''; + + printf('',$app['server']->getIndex()); + printf('',htmlspecialchars($request['dn'])); + + echo ''; + + echo ''; + printf('',_('Add')); + echo '
'; + echo '
'; + + } else { + echo '
'; + printf('(%s)',_('no new attributes available for this entry')); + } + + if (count($attr['binary']) > 0) { + echo '
'; + echo _('Add new binary attribute'); + echo '
'; + echo '
'; + + echo ''; + echo '
'; + echo '
'; + + if ($_SESSION[APPCONFIG]->getValue('confirm','update')) + echo ''; + else + echo ''; + + printf('',$app['server']->getIndex()); + printf('',$request['dn']); + echo ''; + + echo ''; + + echo ''; + printf('',_('Add')); + + if (! ini_get('file_uploads')) + printf('
%s
', + _('Your PHP configuration has disabled file uploads. Please check php.ini before proceeding.')); + + else + printf('
%s: %s
',_('Maximum file size'),ini_get('upload_max_filesize')); + + echo '
'; + echo '
'; + + } else { + echo '
'; + printf('(%s)',_('no new binary attributes available for this entry')); + } + + } else { + echo '
'; + + $request['page']->drawFormStart(); + printf('',$app['server']->getIndex()); + printf('',htmlspecialchars($request['dn'])); + + echo ''; + + foreach ($request['template']->getAvailAttrs() as $attribute) + $request['page']->draw('Template',$attribute); + + $request['page']->drawFormSubmitButton(); + echo '
'; + + $request['page']->drawFormEnd(); + } + + } else { + printf('(%s)',_('no new attributes available for this entry')); + } + + echo '
'; + +# The ajax addition (it is going into an existing TemplateRendered page +} else { + # Put our DIV there for the callback + echo '
'; + printf('%s',_('Add Attribute')); + echo '
'; + echo ''; + echo ''; + echo '
'; + + printf(''; + + echo '
'; + echo '
'; + echo '
'; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/add_oclass_form.php b/lam/templates/3rdParty/pla/htdocs/add_oclass_form.php new file mode 100644 index 00000000..d2353c9c --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/add_oclass_form.php @@ -0,0 +1,117 @@ +dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$request['page'] = new TemplateRender($app['server']->getIndex(),get_request('template','REQUEST',false,null)); +$request['page']->setDN($request['dn']); +$request['page']->accept(true); +$request['template'] = $request['page']->getTemplate(); + +$attribute_factory = new AttributeFactory(); + +# Grab the required attributes for the new objectClass +$ldap = array(); +$ldap['attrs']['must'] = array(); + +foreach ($request['template']->getAttribute('objectclass')->getValues() as $oclass_name) { + # Exclude "top" if its there. + if (! strcasecmp('top',$oclass_name)) + continue; + + if ($soc = $app['server']->getSchemaObjectClass($oclass_name)) + $ldap['attrs']['must'] = array_merge($ldap['attrs']['must'],$soc->getMustAttrNames(true)); +} + +$ldap['attrs']['must'] = array_unique($ldap['attrs']['must']); + +/* Build a list of the attributes that this new objectClass requires, + * but that the object does not currently contain */ +$ldap['attrs']['need'] = array(); +foreach ($ldap['attrs']['must'] as $attr) + if (is_null($request['template']->getAttribute($attr))) + array_push($ldap['attrs']['need'],$attribute_factory->newAttribute($attr,array('values'=>array()),$app['server']->getIndex())); + +# Mark all the need attributes as shown +foreach ($ldap['attrs']['need'] as $index => $values) + $ldap['attrs']['need'][$index]->show(); + +if (count($ldap['attrs']['need']) > 0) { + $request['page']->drawTitle(sprintf('%s %s',_('Add new objectClass to'),get_rdn($request['dn']))); + $request['page']->drawSubTitle(); + + echo '
'; + printf('%s: %s %s %s %s', + _('Instructions'), + _('In order to add these objectClass(es) to this entry, you must specify'), + count($ldap['attrs']['need']),_('new attributes'), + _('that this objectClass requires.')); + + echo '

'; + + echo '
'; + echo '
'; + + if ($_SESSION[APPCONFIG]->getValue('confirm','update')) + echo ''; + else + echo ''; + + printf('',$app['server']->getIndex()); + printf('',htmlspecialchars($request['dn'])); + echo '
'; + + echo ''; + printf('',_('New Required Attributes')); + + $counter = 0; + echo ''; + + foreach ($ldap['attrs']['need'] as $count => $attr) + $request['page']->draw('Template',$attr); + + echo '
%s
'; + foreach ($request['template']->getAttribute('objectclass')->getValues() as $value) + $request['page']->draw('HiddenValue',$request['template']->getAttribute('objectclass'),$counter++); + echo '
'; + + printf('

',_('Add ObjectClass and Attributes')); + echo '
'; + echo '
'; + +# There are no other required attributes, so we just need to add the objectclass to the DN. +} else { + $result = $app['server']->modify($request['dn'],$request['template']->getLDAPmodify()); + + if ($result) { + $href = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s&modified_attrs[]=objectclass', + $app['server']->getIndex(),rawurlencode($request['dn'])); + + if (get_request('meth','REQUEST') == 'ajax') + $href .= '&meth=ajax'; + + header(sprintf('Location: %s',$href)); + die(); + } +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/add_value_form.php b/lam/templates/3rdParty/pla/htdocs/add_value_form.php new file mode 100644 index 00000000..790b6392 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/add_value_form.php @@ -0,0 +1,173 @@ +dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$request['page'] = new TemplateRender($app['server']->getIndex(),get_request('template','REQUEST',false,null)); +$request['page']->setDN($request['dn']); +$request['page']->accept(true); +$request['template'] = $request['page']->getTemplate(); + +/* +if ($request['attribute']->isReadOnly()) + error(sprintf(_('The attribute (%s) is in readonly mode.'),$request['attr']),'error','index.php'); +*/ + +# Render the form +if (! strcasecmp($request['attr'],'objectclass') || get_request('meth','REQUEST') != 'ajax') { + # Render the form. + $request['page']->drawTitle(sprintf('%s %s %s %s',_('Add new'),$request['attr'],_('value to'),get_rdn($request['dn']))); + $request['page']->drawSubTitle(); + + if (! strcasecmp($request['attr'],'objectclass')) { + echo '
'; + echo '
'; + echo ''; + + } else { + echo ''; + echo '
'; + if ($_SESSION[APPCONFIG]->getValue('confirm','update')) + echo ''; + else + echo ''; + } + + printf('',$app['server']->getIndex()); + printf('',htmlspecialchars($request['dn'])); + echo '
'; + + echo ''; + echo ''; + + $request['attribute'] = $request['template']->getAttribute($request['attr']); + $request['count'] = $request['attribute']->getValueCount(); + + if ($request['count']) { + printf('', + _('Current list of'),$request['count'],_('values for attribute'),$request['attribute']->getFriendlyName()); + + echo ''; + + } else { + printf('', + _('No current value for attribute'),$request['attribute']->getFriendlyName()); + echo ''; + } + + echo ''; + + echo ''; + printf('',_('Enter the value(s) you would like to add:')); + echo ''; + echo ''; + + if ($_SESSION[APPCONFIG]->getValue('appearance','show_hints')) + printf('', + IMGDIR,_('Note: You may be required to enter new attributes that these objectClass(es) require')); + + echo '
%s %s %s %s:'; + + # Display current attribute values + echo '
'; + for ($i=0;$i<$request['count'];$i++) { + if ($i > 0) + echo '
'; + $request['page']->draw('CurrentValue',$request['attribute'],$i); + $request['page']->draw('HiddenValue',$request['attribute'],$i); + } + echo '
'; + + echo '
%s %s.

%s'; + + if (! strcasecmp($request['attr'],'objectclass')) { + # If our attr is an objectClass, fetch all available objectClasses and remove those from the list that are already defined in the entry + $socs = $app['server']->SchemaObjectClasses(); + + foreach ($request['attribute']->getValues() as $oclass) + unset($socs[strtolower($oclass)]); + + # Draw objectClass selection + echo ''; + echo '
'; + echo ''; + echo '
'; + + echo '
'; + printf('', + _('Add new ObjectClass'), + (isAjaxEnabled() ? sprintf('onclick="return ajSUBMIT(\'BODY\',document.getElementById(\'entry_form\'),\'%s\');"',_('Updating Object')) : '')); + echo '
'; + echo '

Hint%s
'; + echo ''; + + } else { + # Draw a blank field + echo '
'; + $request['page']->draw('FormValue',$request['attribute'],$request['count']); + echo '
'; + + $sattr = $app['server']->getSchemaAttribute($request['attr']); + + if ($sattr->getDescription()) + printf('%s: %s
',_('Description'),$sattr->getDescription()); + + if ($sattr->getType()) + printf('%s: %s
',_('Syntax'),$sattr->getType()); + + if ($sattr->getMaxLength()) + printf('%s: %s %s
', + _('Maximum Length'),number_format($sattr->getMaxLength()),_('characters')); + + echo '
'; + printf('',_('Add New Value')); + echo '
'; + + echo ''; + echo ''; + echo ''; + } + +} else { + if (is_null($attribute = $request['template']->getAttribute($request['attr']))) { + $request['template']->addAttribute($request['attr'],array('values'=>array())); + $attribute = $request['template']->getAttribute($request['attr']); + $attribute->show(); + + echo ''; + $request['page']->draw('Template',$attribute); + $request['page']->draw('Javascript',$attribute); + echo '
'; + + } else { + $request['count'] = $attribute->getValueCount(); + $request['page']->draw('FormReadWriteValue',$attribute,$request['count']); + } +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/cmd.php b/lam/templates/3rdParty/pla/htdocs/cmd.php new file mode 100644 index 00000000..34f3848f --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/cmd.php @@ -0,0 +1,83 @@ +getIndex()); + +# See if we can render the command +if (trim($www['cmd'])) { + # If this is a READ-WRITE operation, the LDAP server must not be in READ-ONLY mode. + if ($app['server']->isReadOnly() && ! in_array(get_request('cmd','REQUEST'),$app['readwrite_cmds'])) + error(_('You cannot perform updates while server is in read-only mode'),'error','index.php'); + + # If this command has been disabled by the config. + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script',$www['cmd'])) { + system_message(array('title'=>_('Command disabled by the server configuration'), + _('Error'),'body'=>sprintf('%s: %s.',_('The command could not be run'),htmlspecialchars($www['cmd'])),'type'=>'error'),'index.php'); + + $app['script_cmd'] = null; + } +} + +if ($app['script_cmd']) + include $app['script_cmd']; + +# Refresh a frame - this is so that one frame can trigger another frame to be refreshed. +if (isAjaxEnabled() && get_request('refresh','REQUEST') && get_request('refresh','REQUEST') != get_request('frame','REQUEST')) { + echo ''; +} + +# Capture the output and put into the body of the page. +$www['body'] = new block(); +$www['body']->SetBody(ob_get_contents()); +$www['page']->block_add('body',$www['body']); +ob_end_clean(); + +if ($www['meth'] == 'ajax') + $www['page']->show(get_request('frame','REQUEST',false,'BODY'),true,get_request('raw','REQUEST',false,false)); +else + $www['page']->display(); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/collapse.php b/lam/templates/3rdParty/pla/htdocs/collapse.php new file mode 100644 index 00000000..1eb4ced4 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/collapse.php @@ -0,0 +1,27 @@ +getIndex(),'tree'); +$entry = $tree->getEntry($dn); +$entry->close(); +set_cached_item($app['server']->getIndex(),'tree','null',$tree); + +header(sprintf('Location:index.php?server_id=%s&junk=%s#%s', + $app['server']->getIndex(),random_junk(),htmlid($app['server']->getIndex(),$dn))); +die(); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/common.php b/lam/templates/3rdParty/pla/htdocs/common.php new file mode 100644 index 00000000..de4122c5 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/common.php @@ -0,0 +1,14 @@ + diff --git a/lam/templates/3rdParty/pla/htdocs/compare.php b/lam/templates/3rdParty/pla/htdocs/compare.php new file mode 100644 index 00000000..c874dec2 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/compare.php @@ -0,0 +1,188 @@ +getServer(get_request('server_id_src')); +$ldap['DST'] = $_SESSION[APPCONFIG]->getServer(get_request('server_id_dst')); + +if (! $ldap['SRC']->dnExists($request['dnSRC'])) + error(sprintf('%s (%s)',_('No such entry.'),pretty_print_dn($request['dnSRC'])),'error','index.php'); + +if (! $ldap['DST']->dnExists($request['dnDST'])) + error(sprintf('%s (%s)',_('No such entry.'),pretty_print_dn($request['dnDST'])),'error','index.php'); + +$request['pageSRC'] = new PageRender($ldap['SRC']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['pageSRC']->setDN($request['dnSRC']); +$request['pageSRC']->accept(); +$request['templateSRC'] = $request['pageSRC']->getTemplate(); + +$request['pageDST'] = new PageRender($ldap['DST']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['pageDST']->setDN($request['dnDST']); +$request['pageDST']->accept(); +$request['templateDST'] = $request['pageDST']->getTemplate(); + +# Get a list of all attributes. +$attrs_all = array_unique(array_merge($request['templateSRC']->getAttributeNames(),$request['templateDST']->getAttributeNames())); + +$request['pageSRC']->drawTitle(_('Comparing the following DNs')); + +echo '
'; + +echo ''; +echo ''; + +$href = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s', + $ldap['SRC']->getIndex(),rawurlencode($request['dnSRC'])); +printf('', + _('DN'), + htmlspecialchars($href),$request['dnSRC']); + +$href = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s', + $ldap['DST']->getIndex(),rawurlencode($request['dnDST'])); +printf('', + _('DN'), + htmlspecialchars($href),$request['dnDST']); + +echo ''; + +echo ''; +echo ''; +echo ''; + +if (! is_array($attrs_all) || ! count($attrs_all)) { + printf('',_('This entry has no attributes')); + print '
%s: %s%s: %s
'; +echo '
'; +echo '
'; +printf('',$app['server']->getIndex()); +printf('',$ldap['DST']->getIndex()); +printf('',$ldap['SRC']->getIndex()); +printf('',htmlspecialchars($request['dnDST'])); +printf('',htmlspecialchars($request['dnSRC'])); +printf('',_('Switch Entry')); +echo '
'; +echo '
'; +echo '
(%s)
'; + + return; +} + +sort($attrs_all); + +# Work through each of the attributes. +foreach ($attrs_all as $attr) { + # Has the config.php specified that this attribute is to be hidden or shown? + if ($ldap['SRC']->isAttrHidden($attr) || $ldap['DST']->isAttrHidden($attr)) + continue; + + $attributeSRC = $request['templateSRC']->getAttribute($attr); + $attributeDST = $request['templateDST']->getAttribute($attr); + + # Get the values and see if they are the same. + if ($attributeSRC && $attributeDST && ($attributeSRC->getValues() == $attributeDST->getValues())) + echo ''; + else + echo ''; + + foreach (array('src','dst') as $side) { + # If we are on the source side, show the attribute name. + switch ($side) { + case 'src': + if ($attributeSRC) { + echo ''; + $request['pageSRC']->draw('Name',$attributeSRC); + echo ''; + + if ($request['pageSRC']->getServerID() == $request['pageDST']->getServerID()) + echo ' '; + + else { + echo ''; + $request['pageSRC']->draw('Notes',$attributeSRC); + echo ''; + } + + } else { + echo ' '; + } + + break; + + case 'dst': + if ($attributeDST) { + if ($attributeSRC && ($request['pageSRC']->getServerID() == $request['pageDST']->getServerID())) + echo ' '; + + else { + echo ''; + $request['pageDST']->draw('Name',$attributeDST); + echo ''; + } + + echo ''; + $request['pageDST']->draw('Notes',$attributeDST); + echo ''; + + } else { + echo ' '; + } + + break; + } + } + + echo ''; + echo "\n\n"; + + # Get the values and see if they are the same. + if ($attributeSRC && $attributeDST && ($attributeSRC->getValues() == $attributeDST->getValues())) + echo ''; + else + echo ''; + + foreach (array('src','dst') as $side) { + echo ''; + echo ''; + echo '
'; + + switch ($side) { + case 'src': + + if ($attributeSRC && count($attributeSRC->getValues())) + $request['pageSRC']->draw('CurrentValues',$attributeSRC); + else + echo ' '; + + break; + + case 'dst': + if ($attributeDST && count($attributeDST->getValues())) + $request['pageDST']->draw('CurrentValues',$attributeDST); + else + echo ' '; + + break; + } + + echo '
'; + } + + echo ''; +} +echo ''; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/compare_form.php b/lam/templates/3rdParty/pla/htdocs/compare_form.php new file mode 100644 index 00000000..d3292a72 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/compare_form.php @@ -0,0 +1,65 @@ +dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$request['page'] = new PageRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setDN($request['dn']); +$request['page']->accept(); + +# Render the form +$request['page']->drawTitle(sprintf('%s %s',_('Compare another DN with'),get_rdn($request['dn']))); +$request['page']->drawSubTitle(); + +printf('',JSDIR); +echo '
'; +printf('%s %s %s
',_('Compare'),get_rdn($request['dn']),_('with ')); +echo '
'; + +echo '
'; +echo '
'; +echo ''; +printf('',$app['server']->getIndex()); +printf('',$app['server']->getIndex()); +printf('',htmlspecialchars($request['dn'])); +echo '
'; +echo "\n"; + +echo ''; + +echo ''; +printf('', + _('Compare this DN with another'),_('Destination DN')); +echo ''; +echo ''; +echo "\n"; + +printf('',server_select_list($app['server']->getIndex(),true,'server_id_dst')); +echo "\n"; + +printf('',_('Compare')); +echo "\n"; + +echo '
%s:'; +echo ''; +draw_chooser_link('compare_form','dn_dst','true',''); +echo '
%s
'; +echo '
'; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/copy.php b/lam/templates/3rdParty/pla/htdocs/copy.php new file mode 100644 index 00000000..588a1e0d --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/copy.php @@ -0,0 +1,209 @@ +getServer(get_request('server_id_src')); +$ldap['DST'] = $_SESSION[APPCONFIG]->getServer(get_request('server_id_dst')); + +# Error checking +if (! trim($request['dnDST'])) + error(_('You left the destination DN blank.'),'error','index.php'); + +if ($ldap['DST']->isReadOnly()) + error(_('Destination server is currently READ-ONLY.'),'error','index.php'); + +if ($ldap['DST']->dnExists($request['dnDST'])) + error(sprintf(_('The destination entry (%s) already exists.'),pretty_print_dn($request['dnDST'])),'error','index.php'); + +if (! $ldap['DST']->dnExists($ldap['DST']->getContainer($request['dnDST']))) + error(sprintf(_('The destination container (%s) does not exist.'), + pretty_print_dn($ldap['DST']->getContainer($request['dnDST']))),'error','index.php'); + +if (pla_compare_dns($request['dnSRC'],$request['dnDST']) == 0 && $ldap['SRC']->getIndex() == $ldap['DST']->getIndex()) + error(_('The source and destination DN are the same.'),'error','index.php'); + +$request['recursive'] = (get_request('recursive') == 'on') ? true : false; +$request['remove'] = (get_request('remove') == 'yes') ? true : false; + +if ($request['recursive']) { + $filter = get_request('filter','POST',false,'(objectClass=*)'); + + # Build a tree similar to that of the tree browser to give to r_copy_dn + $ldap['tree'] = array(); + printf('

%s%s

',_('Copying '),$request['dnSRC']); + printf('

%s

',_('Recursive copy progress')); + print '

'; + + print ''; + printf ('%s...',_('Building snapshot of tree to copy')); + + $ldap['tree'] = build_tree($ldap['SRC'],$request['dnSRC'],array(),$filter); + printf('%s
',_('Success')); + + # Prevent script from bailing early on a long delete + @set_time_limit(0); + + $copy_result = r_copy_dn($ldap['SRC'],$ldap['DST'],$ldap['tree'],$request['dnSRC'],$request['dnDST'],$request['remove']); + $copy_message = $copy_result; + print '
'; + +} else { + if ($_SESSION[APPCONFIG]->getValue('confirm','copy')) { + $request['pageSRC'] = new TemplateRender($app['server']->getIndex(),get_request('template','REQUEST',false,null)); + $request['pageSRC']->setDN($request['dnSRC']); + $request['pageSRC']->accept(true); + + $request['pageDST'] = new TemplateRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none')); + $request['pageDST']->setContainer($app['server']->getContainer($request['dnDST'])); + $request['pageDST']->accept(true); + + $request['templateSRC'] = $request['pageSRC']->getTemplate(); + $request['templateDST'] = $request['pageDST']->getTemplate(); + + $request['templateDST']->copy($request['templateSRC'],get_rdn($request['dnDST']),true); + + # Set all attributes with a values as shown, and remove the add value options + foreach ($request['templateDST']->getAttributes(true) as $sattribute) + if ($sattribute->getValues() && ! $sattribute->isInternal()) { + $sattribute->show(); + $sattribute->setMaxValueCount(count($sattribute->getValues())); + } + + $request['pageDST']->accept(); + + return; + + } else { + $copy_result = copy_dn($ldap['SRC'],$ldap['DST'],$request['dnSRC'],$request['dnDST'],$request['remove']); + + if ($copy_result) + $copy_message = sprintf('%s %s: %s %s', + $request['remove'] ? _('Move successful') : _('Copy successful'), + _('DN'),$request['dnDST'],_('has been created.')); + else + $copy_message = sprintf('%s %s: %s %s', + $request['remove'] ? _('Move NOT successful') : _('Copy NOT successful'), + _('DN'),$request['dnDST'],_('has NOT been created.')); + } +} + +if ($copy_result) { + $redirect_url = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s&refresh=SID_%s_nodes&noheader=1', + $ldap['DST']->getIndex(),rawurlencode($request['dnDST']),$ldap['DST']->getIndex()); + + system_message(array( + 'title'=>_('Copy Entry'), + 'body'=>$copy_message, + 'type'=>'info'), + $redirect_url); +} + +function r_copy_dn($serverSRC,$serverDST,$snapshottree,$dnSRC,$dnDST,$remove) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $copy_message = array(); + + $children = isset($snapshottree[$dnSRC]) ? $snapshottree[$dnSRC] : null; + + # If we have children, then we need to copy, then delete for a move + if (is_array($children) && count($children)) { + + $copy_result = copy_dn($serverSRC,$serverDST,$dnSRC,$dnDST,false); + + if (! $copy_result) + return false; + + array_push($copy_message,sprintf('%s %s: %s %s',_('Copy successful'),_('DN'),$dnDST,_('has been created.'))); + + $hadError = false; + foreach ($children as $child_dn) { + $dnDST_new = sprintf('%s,%s',get_rdn($child_dn),$dnDST); + $copy_result = r_copy_dn($serverSRC,$serverDST,$snapshottree,$child_dn,$dnDST_new,$remove); + $copy_message = array_merge($copy_message,array_values($copy_result)); + + if (! $copy_result) + $hadError = true; + } + + if (! $hadError && $remove) { + $delete_result = $serverSRC->delete($dnSRC); + + if ($delete_result) + array_push($copy_message,sprintf('%s %s: %s %s',_('Delete successful'),_('DN'),$dnDST,_('has been deleted.'))); + } + + } else { + $copy_result = copy_dn($serverSRC,$serverDST,$dnSRC,$dnDST,$remove); + + if ($copy_result) + array_push($copy_message,sprintf('%s %s: %s %s', + $remove ? _('Move successful') : _('Copy successful'), + _('DN'),$dnDST,_('has been created.'))); + else + array_push($copy_message,sprintf('%s %s: %s %s', + $remove ? _('Move NOT successful') : _('Copy NOT successful'), + _('DN'),$dnDST,_('has NOT been created.'))); + } + + return $copy_message; +} + +function copy_dn($serverSRC,$serverDST,$dnSRC,$dnDST,$remove) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $request = array(); + $request['pageSRC'] = new PageRender($serverSRC->getIndex(),get_request('template','REQUEST',false,'none')); + $request['pageSRC']->setDN($dnSRC); + $request['pageSRC']->accept(); + + $request['pageDST'] = new PageRender($serverDST->getIndex(),get_request('template','REQUEST',false,'none')); + $request['pageDST']->setContainer($serverDST->getContainer($dnDST)); + $request['pageDST']->accept(); + + $request['templateSRC'] = $request['pageSRC']->getTemplate(); + $request['templateDST'] = $request['pageDST']->getTemplate(); + $request['templateDST']->copy($request['pageSRC']->getTemplate(),get_rdn($dnDST,0)); + + # Create of move the entry + if ($remove) + return $serverDST->rename($request['templateSRC']->getDN(),$request['templateDST']->getRDN(),$serverDST->getContainer($dnDST),true); + else + return $serverDST->add($request['templateDST']->getDN(),$request['templateDST']->getLDAPadd()); +} + +function build_tree($server,$dn,$buildtree) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # We search all children, not only the visible children in the tree + $children = $server->getContainerContents($dn,null,0); + + if (count($children)) { + $buildtree[$dn] = $children; + foreach ($children as $child_dn) + $buildtree = build_tree($server,$child_dn,$buildtree); + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$buildtree); + + return $buildtree; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/copy_form.php b/lam/templates/3rdParty/pla/htdocs/copy_form.php new file mode 100644 index 00000000..749b78ec --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/copy_form.php @@ -0,0 +1,103 @@ +dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$request['page'] = new PageRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setDN($request['dn']); +$request['page']->accept(); + +# Render the form +$request['page']->drawTitle(sprintf('%s %s',_('Copy'),get_rdn($request['dn']))); +$request['page']->drawSubTitle(); + +printf('',JSDIR); +echo '
'; +printf('%s %s %s:

',_('Copy'),get_rdn($request['dn']),_('to a new object')); +echo '
'; + +echo '
'; +echo '
'; +echo ''; +printf('',$app['server']->getIndex()); +printf('',$app['server']->getIndex()); +printf('',htmlspecialchars($request['dn'])); +echo '
'; +echo "\n"; + +echo ''; + +echo ''; +printf('', + _('The full DN of the new entry to be created when copying the source entry'),_('Destination DN')); +echo ''; +echo ''; +echo "\n"; + +printf('',_('Destination Server'),server_select_list($app['server']->getIndex(),true,'server_id_dst')); +echo "\n"; + +# We search all children, not only the visible children in the tree +$request['children'] = $app['server']->getContainerContents($request['dn']); + +if (count($request['children']) > 0) { + echo ''; + printf('',_('Recursive copy')); + echo '',_('Recursively copy all children of this object as well.')); + echo ''; + echo "\n"; + + echo ''; + printf('', + _('When performing a recursive copy, only copy those entries which match this filter'),_('Filter')); + echo ''; + echo "\n"; + + echo ''; + printf('',_('Delete after copy (move):')); + echo ''; + echo "\n"; + +} else { + printf('',_('Delete after copy (move):')); +} +echo "\n"; + +printf('',_('Copy ')); +echo "\n"; + +echo '
%s:'; +printf('',htmlspecialchars($request['dn'])); +draw_chooser_link('copy_form','dn_dst','true',get_rdn($request['dn'])); +echo '
%s:%s
:'; + printf('(%s)
%s:'; + echo '
%s'; + printf('(%s)',_('Make sure your filter (above) will select all child records.')); + echo '
%s
'; +echo '
'; + +if ($_SESSION[APPCONFIG]->getValue('appearance','show_hints')) + printf('
Light%s
', + IMGDIR,_('Hint: Copying between different servers only works if there are no schema violations')); + + +# Draw the javascrpt to enable/disable the filter field if this may be a recursive copy +if (count($request['children']) > 0) + printf('',JSDIR); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/create.php b/lam/templates/3rdParty/pla/htdocs/create.php new file mode 100644 index 00000000..45944eb7 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/create.php @@ -0,0 +1,104 @@ +getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setContainer(get_request('container','REQUEST',true)); +$request['page']->accept(); +$request['template'] = $request['page']->getTemplate(); + +if ((! $request['template']->getContainer() || ! $app['server']->dnExists($request['template']->getContainer())) && ! get_request('create_base')) + error(sprintf(_('The container you specified (%s) does not exist. Please try again.'),$request['template']->getContainer()),'error','index.php'); + +# Check if the container is a leaf - we shouldnt really return a hit here, the template engine shouldnt have allowed a user to attempt to create an entry... +$tree = get_cached_item($app['server']->getIndex(),'tree'); + +$request['container'] = $tree->getEntry($request['template']->getContainer()); +if (! $request['container'] && ! get_request('create_base')) { + $tree->addEntry($request['template']->getContainer()); + $request['container'] = $tree->getEntry($request['template']->getContainer()); +} + +# Check our RDN +if (! count($request['template']->getRDNAttrs())) + error(_('The were no attributes marked as an RDN attribute.'),'error','index.php'); +if (! $request['template']->getRDN()) + error(_('The RDN field is empty?'),'error','index.php'); + +# Some other attribute checking... +foreach ($request['template']->getAttributes() as $attribute) { + # Check that our Required Attributes have a value - we shouldnt really return a hit here, the template engine shouldnt have allowed this to slip through. + # @todo this isIgnoredAttr() function is missing? + if ($attribute->isRequired() && ! count($attribute->getValues()) && ! $app['server']->isIgnoredAttr($attr->getName())) + error(sprintf(_('You left the value blank for required attribute (%s).'), + $attribute->getName(false)),'error','index.php'); +} + +# Create the entry +$add_result = $app['server']->add($request['template']->getDN(),$request['template']->getLDAPadd()); + +if ($add_result) { + $action_number = $_SESSION[APPCONFIG]->getValue('appearance','action_after_creation'); + $href = sprintf('cmd=template_engine&server_id=%s',$app['server']->getIndex()); + + if ($request['redirect']) + $redirect_url = $request['redirect']; + + else if ($action_number == 2) + $redirect_url = sprintf('cmd.php?%s&template=%s&container=%s', + $href,'default',$request['template']->getContainerEncode()); + + else + $redirect_url = sprintf('cmd.php?%s&template=%s&dn=%s', + $href,'default',$request['template']->getDNEncode()); + + if ($action_number == 1 || $action_number == 2) + printf('',$redirect_url); + + if ($action_number == 1 || $action_number == 2) { + $create_message = sprintf('%s %s: %s %s', + _('Creation successful!'),_('DN'),$request['template']->getDN(),_('has been created.')); + + if (isAjaxEnabled()) + $redirect_url .= sprintf('&refresh=SID_%s_nodes&noheader=1',$app['server']->getIndex()); + + system_message(array( + 'title'=>_('Create Entry'), + 'body'=>$create_message, + 'type'=>'info'), + $redirect_url); + + } else { + $request['page']->drawTitle(_('Entry created')); + $request['page']->drawSubTitle(sprintf('%s: %s', + _('DN'),$request['template']->getDN())); + + echo '
'; + echo '
'; + printf('%s.', + htmlspecialchars($href),rawurlencode($request['template']->getDN()),_('Display the new created entry')); + echo '
'; + printf('%s.', + htmlspecialchars($href),rawurlencode($request['template']->getContainer()),_('Create another entry')); + echo '
'; + } +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/create_confirm.php b/lam/templates/3rdParty/pla/htdocs/create_confirm.php new file mode 100644 index 00000000..6727fb15 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/create_confirm.php @@ -0,0 +1,143 @@ +getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setContainer(get_request('container','REQUEST',true)); +$request['page']->accept(); +$request['template'] = $request['page']->getTemplate(); + +if (! $request['template']->getContainer() || ! $app['server']->dnExists($request['template']->getContainer())) + error(sprintf(_('The container you specified (%s) does not exist. Please try again.'),$request['template']->getContainer()),'error','index.php'); + +# Check if the container is a leaf - we shouldnt really return a hit here, the template engine shouldnt have allowed a user to attempt to create an entry... +$tree = get_cached_item($app['server']->getIndex(),'tree'); + +$request['container'] = $tree->getEntry($request['template']->getContainer()); +if (! $request['container']) { + $tree->addEntry($request['template']->getContainer()); + $request['container'] = $tree->getEntry($request['template']->getContainer()); +} + +# Check our RDN +if (! count($request['template']->getRDNAttrs())) + error(_('The were no attributes marked as an RDN attribute.'),'error','index.php'); +if (! $request['template']->getRDN()) + error(_('The RDN field is empty?'),'error','index.php'); + +# Some other attribute checking... +foreach ($request['template']->getAttributes() as $attribute) { + # Check that our Required Attributes have a value - we shouldnt really return a hit here, the template engine shouldnt have allowed this to slip through. + # @todo this isIgnoredAttr() function is missing? + if ($attribute->isRequired() && ! count($attribute->getValues()) && ! $app['server']->isIgnoredAttr($attr->getName())) + error(sprintf(_('You left the value blank for required attribute (%s).'), + $attribute->getName(false)),'error','index.php'); +} + +# Check for unique attributes +$app['server']->checkUniqueAttrs($request['template']->getDN(),$request['template']->getLDAPadd()); + +$request['page']->drawTitle(_('Create LDAP Entry')); +$request['page']->drawSubTitle(sprintf('%s: %s   %s: %s', + _('Server'),$app['server']->getName(),_('Container'),$request['template']->getContainer())); + +# Confirm the creation +if (count($request['template']->getLDAPadd(true))) { + echo '
'; + echo _('Do you want to create this entry?'); + echo '

'; + echo '
'; + + echo "\n\n"; + echo '
'; + echo '
'; + echo ''; + printf('',$app['server']->getIndex()); + printf('',$request['template']->getContainerEncode(false)); + printf('',$request['template']->getID()); + foreach ($request['template']->getRDNAttrs() as $rdn) + printf('',htmlspecialchars($rdn)); + echo "\n"; + + $request['page']->drawHiddenAttributes(); + echo '
'; + + echo ''; + echo "\n"; + + printf('', + _('Attribute'),_('New Value'),_('Skip')); + echo "\n\n"; + + $counter = 0; + printf('',$counter%2 ? 'even' : 'odd',htmlspecialchars($request['template']->getDN())); + + foreach ($request['template']->getLDAPadd(true) as $attribute) { + $counter++; + + printf('',$counter%2 ? 'even' : 'odd'); + printf('',$attribute->getFriendlyName()); + + # Show NEW Values + echo ''; + + # Show SKIP Option + $input_disabled = ''; + $input_onclick = ''; + + if ($attribute->isRequired()) + $input_disabled = 'disabled="disabled"'; + + printf('', + htmlspecialchars($attribute->getName()),htmlspecialchars($attribute->getName()),$input_disabled,$input_onclick); + echo ''; + echo "\n\n"; + } + + echo '
%s%s%s
%s
%s'; + $request['page']->draw('CurrentValues',$attribute); + echo '
'; + + echo '
'; + echo '
'; + + printf('', + _('Commit'), + (isAjaxEnabled() ? sprintf('onclick="return ajSUBMIT(\'BODY\',document.getElementById(\'create_form\'),\'%s\');"',_('Updating Object')) : '')); + + printf('', + _('Cancel'), + (isAjaxEnabled() ? sprintf('onclick="return ajDISPLAY(\'BODY\',\'cmd=template_engine&server_id=%s&container=%s\',\'%s\');"',$app['server']->getIndex(),$request['template']->getContainer(),_('Retrieving DN')) : '')); + + echo '
'; + echo '
'; + echo '
'; + +} else { + $href = sprintf('cmd=template_engine&server_id=%s&dn=%s', + $app['server']->getIndex(),$request['template']->getDNEncode()); + + echo '
'; + echo _('You made no changes'); + + if (isAjaxEnabled()) + printf(' %s.', + htmlspecialchars($href),htmlspecialchars($href),_('Retrieving DN'),_('Go back')); + else + printf(' %s.',htmlspecialchars($href),_('Go back')); + + echo '
'; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/css/default/style.css b/lam/templates/3rdParty/pla/htdocs/css/default/style.css new file mode 100644 index 00000000..4cc37e50 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/css/default/style.css @@ -0,0 +1,915 @@ +/* $Header$ */ + +/* Global Page */ +table.page { + font-weight: normal; + color: #000000; + + font-family: "bitstream vera sans","luxi sans",verdana,geneva,arial,helvetica,sans-serif; + background-color: #F5F5F5; + font-size: 13px; + empty-cells: hide; +} + +/* Global Page - Defaults */ +/* A HREF Links */ +table.page a { + color: #0000AA; + text-decoration: none; +} + +table.page a:hover { + text-decoration: none; +} + +table.page a img { + border: 0px; +} + +/* Global Page - Logo & Title */ +table.page tr.head { + text-align: center; + color: #FFFFFF; + background-color: #001188; + font-weight: bold; + font-size: 11px; + height: 25px; +} + +table.page tr.head img.logo { + vertical-align: middle; + text-align: center; + + width: 100px; + height: 60px; +} + +table.page tr.pagehead { +} + +table.page tr.pagehead td.imagetop { + width: 100%; + vertical-align: bottom; + text-align: right; +} + +/* Global Page - Control Line */ +table.page tr.control td { + border-top: 1px solid #AAAACC; + border-bottom: 1px solid #AAAACC; +} + +/* Global Page - Control Line Menu Items */ +table.page table.control { + table-layout: fixed; + width: 100%; +} + +table.page table.control td { + border-top: 0px; + border-bottom: 0px; + padding: 0px; + padding-top: 2px; + padding-bottom: 2px; + text-align: left; + vertical-align: top; + font-size: 11px; + font-weight: bold; +} + +table.page table.control img { + width: 24px; + height: 24px; +} + +table.page table.control a { + color: #000000; +} + +table.page table.control a:hover { + text-decoration: none; + background-color: #FFFFFF; + color: #0000AA; +} + +table.page table.control td.spacer { + width: 20%; +} + +table.page table.control td.logo { + text-align: right; + width: 10%; +} + +table.page table.control td.logo img.logo { + vertical-align: middle; + text-align: right; + + width: 100px; + height: 50px; +} + +/* Global Page - LDAP Tree */ +table.page td.tree { + border-right: 1px solid #AAAACC; + vertical-align: top; + background-color: #F5F5F5; + width: 10%; +} + +/* Global Page - Main Body */ +table.page td.body { + vertical-align: top; + width: 100%; + background-color: #F5F5F5; +} + +/* Global Page - Main Body System Message */ +table.page table.sysmsg { + border-bottom: 2px solid #AAAACC; + width: 100%; +} + +table.page table.sysmsg td.head { + font-size: small; + text-align: left; + font-weight: bold; +} + +table.page table.sysmsg td.body { + font-weight: normal; +} + +table.page table.sysmsg td.icon { + text-align: center; + vertical-align: top; +} + +/* Global Page - Main Body */ +table.page table.body { + font-weight: normal; + background-color: #F5F5F5; + width: 100%; +} + +table.page table.body h3.title { + text-align: center; + margin: 0px; + padding: 10px; + color: #FFFFFF; + background-color: #000088; + border: 1px solid #000000; + font-weight: normal; + font-size: 150%; +} + +table.page table.body h3.subtitle { + text-align: center; + margin: 0px; + margin-bottom: 15px; + font-size: 75%; + color: #FFFFFF; + border-bottom: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + background: #000088; + padding: 4px; + font-weight: normal; +} + +table.page table.body td.spacer { + border-top: 2px solid #AAAACC; + padding: 0px; + font-size: 5px; +} + +table.page table.body td.head { + font-weight: bold; +} + +table.page table.body td.foot { + font-size: small; + border-top: 1px solid #AAAACC; + border-bottom: 1px solid #AAAACC; +} + +/* Global Page Footer */ +table.page tr.foot td { + border-top: 1px solid #AAAACC; + font-weight: bold; + font-size: 12px; + text-align: right; +} + +/* Global Page - Other Layouts */ +/* Server Select */ +table.page table.server_select { + font-weight: bold; + font-size: 13px; + color: #000000; +} + +/* Individual table layouts */ +/* LDAP Tree */ +table.tree { +} + +table.tree tr.server td.icon { + vertical-align: top; +} + +table.tree tr.server td.name { + padding-right: 10px; + vertical-align: top; +} + +table.tree tr.server td { + padding-top: 5px; + font-size: 18px; + text-align: left; + padding-right: 0px; + white-space: nowrap; +} + +table.tree td { + white-space: nowrap; +} + +table.tree td.server_links { + vertical-align: top; + text-align: center; + padding-top: 0px; + padding-bottom: 0px; + padding-left: 3px; + padding-right: 3px; +} + +table.tree td.server_links a { + color: #000000; + text-decoration: none; + font-size: 11px; +} + +table.tree td.server_links a:hover { + text-decoration: none; + background-color: #FFFFFF; + color: #000000; +} + +table.tree tr.option td.expander { + text-align: center; + width: 22px; + max-width: 22px; + min-width: 22px; + white-space: nowrap; +} + +table.tree tr.option td.icon { + text-align: center; + width: 22px; + max-width: 22px; + min-width: 22px; + white-space: nowrap; +} + +table.tree td.rdn a { + font-size: 13px; + color: #000000; +} + +table.tree td.rdn a:hover { + font-size: 13px; + color: #841212; + background-color: #FFF0C0; + text-decoration: none; +} + +table.tree td.rdn span.count { + font-size: 13px; + color: #000000; +} + +table.tree td.links a { + color: #0000AA; + text-align: center; +} + +table.tree td.link a { + font-size: 13px; + color: #000000; +} + +table.tree td.link a:hover { + font-size: 13px; + color: #841212; + background-color: #FFF0C0; + text-decoration: none; +} + +table.tree td.links a:hover { + text-decoration: none; + color: blue; +} + +table.tree td.blank { + font-size: 1px; +} + +table.tree td.spacer { + width: 22px; +} + +table.tree td.logged_in { + font-size: 10px; + white-space: nowrap; +} + +table.tree td.logged_in a { + font-size: 11px; +} + +table.tree td.logged_in a:hover { + color: #841212; + background-color: #FFF0C0; + text-decoration: none; +} + +/* Tree Global Defaults */ +table.tree tr td { + padding: 0px; +} + +table.tree a { + text-decoration: none; + color: #000000; +} + +table.tree a:hover { + text-decoration: underline; + color: blue; +} + +table.tree span.dnicon img { + width: 16px; + padding-bottom: 0px; +} + +/* Tree */ +table.tree .treemenudiv { + display: block; + white-space: nowrap; + padding-top: 1px; + padding-bottom: 1px; +} + +table.tree .phplmnormal { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #000000; + text-decoration: none; +} + +table.tree a.phplmnormal:hover { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #000000; + background-color: #fff0c0; + text-decoration: none; +} + +table.tree a.phplm:link { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #000000; + text-decoration: none; +} + +table.tree a.phplm:visited { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #000000; + text-decoration: none; +} + +table.tree a.phplm:hover { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #841212; + background-color: #fff0c0; + text-decoration: none; +} + +table.tree a.phplm:active { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #ff0000; + text-decoration: none; +} + +table.tree a.phplmselected:link { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #dd0000; + background-color: #ffdd76; + text-decoration: none; +} + +table.tree a.phplmselected:visited { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #dd0000; + background-color: #ffdd76; + text-decoration: none; +} + +table.tree a.phplmselected:hover { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #841212; + background-color: #fff0c0; + text-decoration: none; +} + +table.tree a.phplmselected:active { + font-family: bitstream vera sans, luxi sans, verdana, geneva, arial, helvetica, sans-serif; + font-size: 13px; + color: #ff0000; + text-decoration: none; +} + +/* Standard Form */ +table.forminput { + background-color: #F9F9FA; + padding: 10px; + border: 1px solid #AAAACC; +} + +table.forminput td.title { + text-align: center; + font-weight: bold; +} + +table.forminput td.subtitle { + text-align: center; + font-weight: normal; + font-size: small; +} + +table.forminput tr td.heading { + font-weight: bold; +} + +table.forminput td.small { + font-size: 80%; +} + +table.forminput td.top { + vertical-align: top; +} + +table.forminput input.val { + width: 350px; + border: 1px solid #AAAACC; +} + +table.forminput input.roval { + width: 350px; + border: none; +} + +table.forminput td.icon { + width: 16px; + text-align: center; +} + +table.forminput td.icon img { + border: 0px; +} + +table.forminput td.label { + text-align: left; + font-size: 13px; +} + +/* Menu on top of entry form */ +table.menu { + font-size: 14px; +} + +table.menu td.icon { + width: 16px; + text-align: center; +} + +/* Edit DN */ +div.add_value { + font-size: 12px; + margin: 0px; + padding: 0px; +} + +/* Edit Entry */ +table.entry { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; +} + +table.entry input { + margin: 1px; +} + +table.entry input.value { + color: #000000; + font-size: 14px; + width: 350px; + background-color: #FFFFFF; +} + +table.entry div.helper { + text-align: left; + white-space: nowrap; + background-color: #FFFFFF; + color: #888; + font-size: 14px; + font-weight: normal; +} + +table.entry input.roval { + font-size: 14px; + width: 350px; + background-color: #FFFFFF; + color: #000000; + border: none; +} + +table.entry textarea.value { + font-size: 14px; + width: 350px; + background-color: #FFFFFF; + color: #000000; +} + +table.entry textarea.roval { + font-size: 14px; + width: 350px; + background-color: #FFFFFF; + color: #000000; + border: none; +} + +table.entry tr td { + padding: 4px; + padding-right: 0px; +} + +table.entry tr td.heading { + border-top: 3px solid #C0C0C0; + font-weight: bold; +} + +table.entry tr td.note { + text-align: right; + background-color: #E0E0E0; +} + +table.entry tr td.title { + background-color: #E0E0E0; + vertical-align: top; + font-weight: bold; +} + +table.entry tr td.title a { + text-decoration: none; + color: #000000; +} + +table.entry tr td.title a:hover { + text-decoration: underline; + color: #016; +} + +table.entry tr td.value { + text-align: left; + vertical-align: middle; + padding-bottom: 10px; + padding-left: 50px; +} + +/** When an attr is updated, it is highlighted to indicate such */ +table.entry tr.updated td.title { + border-top: 1px dashed #AAAA88; + border-left: 1px dashed #AAAA88; + background-color: #999988; +} + +table.entry tr.updated td.note { + border-top: 1px dashed #AAAA88; + border-right: 1px dashed #AAAA88; + background-color: #999988; +} + +/** An extra row that sits at the bottom of recently modified attrs to encase them in dashes */ +table.entry tr.updated td.bottom { + border-top: 1px dashed #AAAA88; +} + +/** Formatting for the value cell when it is the attribute that has been recently modified */ +table.entry tr.updated td.value { + border-left: 1px dashed #AAAA88; + border-right: 1px dashed #AAAA88; +} + +/* Need to prevent sub-tables (like the one in which jpegPhotos are displayed) + * from drawing borders as well. */ +table.entry tr.updated td table td { + border: 0px; +} + +table.entry tr.noinput { + background: #E0E0E0; +} + +span.hint { + font-size: small; + font-weight: normal; + color: #888; +} + +/* Login Box */ +#login { + background: url('../../images/default/ldap-uid.png') no-repeat 0 1px; + background-color: #FAFAFF; + color: #000000; + padding-left: 17px; +} + +#login:focus { + background-color: #F0F0FF; + color: #000000; +} + +#login:disabled { + background-color: #DDDDFF; + color: #000000; +} + +#password { + background: url('../../images/default/key.png') no-repeat 0 1px; + background-color: #FAFAFF; + color: #000000; + padding-left: 17px; +} + +#password:focus { + background-color: #F0F0FF; + color: #000000; +} + +#password:disabled { + background-color: #DDDDFF; + color: #000000; +} + +#generic { + background-color: #FAFAFF; + color: #000000; + padding-left: 17px; +} +#generic:focus { + background-color: #F0F0FF; + color: #000000; +} + +#generic:disabled { + background-color: #DDDDFF; + color: #000000; +} + +/* After input results */ +div.execution_time { + font-size: 75%; + font-weight: normal; + text-align: left; +} + +table.result { + width: 100%; + vertical-align: top; + empty-cells: show; + border: 1px solid #AAAACC; + border-spacing: 0px; + background-color: #F2F2FF; +} + +table.result tr.heading { + vertical-align: top; +} + +table.result tr.list_title { + background-color: #FFFFFF; +} + +table.result tr.list_title td.icon { + text-align: center; + vertical-align: top; +} + +table.result tr.list_item { + background-color: #FFFFFF; +} + +table.result tr.list_item td.blank { + width: 25px; +} + +table.result tr.list_item td.heading { + vertical-align: top; + color: gray; + width: 10%; + font-size: 12px; +} + +table.result tr.list_item td.value { + color: #000000; + font-size: 12px; +} + +table.result_box { + border: 1px solid #AAAACC; + border-collapse: collapse; + empty-cells: show; +} + +table.result_table { + border: 1px solid #AAAACC; + border-collapse: collapse; + empty-cells: show; +} + +table.result_table td { + font-size: 12px; + vertical-align: top; + border: 1px solid #AAAACC; + padding: 4px; +} + +table.result_table th { + border: 1px solid #AAAACC; + padding: 10px; + padding-left: 20px; + padding-right: 20px; +} + +table.result_table tr.highlight { + background-color: #EEEBBB; +} + +table.result_table tr.highlight td { + border: 1px solid #AAAACC; + font-weight: bold; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; +} + +table.result_table td.heading { + color: #FFFFFF; + background-color: #000088; + font-size: 12px; +} + +table.result_table td.value { + color: #000000; + background-color: #E0E0E0; +} + +table.result_table tr.heading { + color: #FFFFFF; + background-color: #000088; + font-size: 12px; + font-weight: bold; +} + +table.result_table tr.heading a { + color: #FFFFFF; + font-size: 12px; + font-weight: bold; +} + +table.result_table tr.heading td { + border: 1px solid #AAAACC; + font-weight: normal; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; +} + +table.result_table tr.even { + background-color: #E0E0E0; +} + +table.result_table tr.even td { + border: 1px solid #AAAACC; + font-weight: normal; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; +} + +table.result_table tr.even td.title { + font-weight: bold; +} + +table.result_table tr.odd { + background-color: #F0F0F0; +} + +table.result_table tr.odd td { + border: 1px solid #AAAACC; + font-weight: normal; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; +} + +table.result_table tr.odd td.title { + font-weight: bold; +} + +table.result_table ul.list { + margin: 5px; + margin-left: 0px; + padding-left: 20px; +} + +table.result_table ul.list li { + margin-left: 0px; + padding-left: 0px; +} + +table.result_table ul.list li small { + font-size: 75%; + color: #707070; +} + +table.result_table ul.list li small a { + color: #7070C0; +} + +/* Error Dialog Box */ +table.error { + width: 500px; + border: 1px solid #AA0000; + background-color: #FFF0F0; +} + +table.error th { + background-color: #AA0000; + border: 0px; + color: #FFFFFF; + font-size: 14px; + font-weight: bold; + text-align: center; + vertical-align: middle; + width: 100%; +} + +table.error th.img { + vertical-align: middle; + text-align: center; +} + +table.error td { + border: 0px; + background-color: #FFF0F0; + padding: 2px; + text-align: left; + vertical-align: top; +} + +/* Popup Window */ +div.popup h3.subtitle { + text-align: center; + margin: 0px; + margin-bottom: 15px; + color: #FFFFFF; + border-bottom: 1px solid #000000; + border-left: 1px solid #000000; + border-right: 1px solid #000000; + background: #000088; + padding: 4px; + font-weight: normal; +} + +span.good { + color: green; +} + +span.bad { + color: red; +} diff --git a/lam/templates/3rdParty/pla/htdocs/delete.php b/lam/templates/3rdParty/pla/htdocs/delete.php new file mode 100644 index 00000000..137055ca --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/delete.php @@ -0,0 +1,40 @@ +dnExists($request['dn'])) + error(sprintf('%s (%s)',_('No such entry.'),''.pretty_print_dn($request['dn']).''),'error','index.php'); + +# Delete the entry. +$result = $app['server']->delete($request['dn']); + +if ($result) { + $redirect_url = ''; + + if (isAjaxEnabled()) + $redirect_url .= sprintf('&refresh=SID_%s_nodes&noheader=1',$app['server']->getIndex()); + + system_message(array( + 'title'=>_('Delete DN'), + 'body'=>_('Successfully deleted DN ').sprintf('%s',$request['dn']), + 'type'=>'info'), + sprintf('index.php?server_id=%s%s',$app['server']->getIndex(),$redirect_url)); +} else + system_message(array( + 'title'=>_('Could not delete the entry.').sprintf(' (%s)',pretty_print_dn($request['dn'])), + 'body'=>ldap_error_msg($app['server']->getErrorMessage(null),$app['server']->getErrorNum(null)), + 'type'=>'error')); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/delete_attr.php b/lam/templates/3rdParty/pla/htdocs/delete_attr.php new file mode 100644 index 00000000..ad323fa1 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/delete_attr.php @@ -0,0 +1,49 @@ +isAttrReadOnly($request['attr'])) + error(sprintf(_('The attribute "%s" is flagged as read-only in the phpLDAPadmin configuration.'),$request['attr']),'error','index.php'); + +$update_array = array(); +$update_array[$request['attr']] = $app['server']->getDNAttrValue($request['dn'],$request['attr']); + +$redirect_url = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s', + $app['server']->getIndex(),rawurlencode($request['dn'])); + +if (! isset($update_array[$request['attr']][$request['index']])) + system_message(array( + 'title'=>_('Could not delete attribute value.'), + 'body'=>sprintf('%s. %s/%s',_('The attribute value does not exist'),$request['attr'],$request['index']), + 'type'=>'warn'),$redirect_url); + +else { + unset($update_array[$request['attr']][$request['index']]); + foreach ($update_array as $key => $values) + $update_array[$key] = array_values($values); + + $result = $app['server']->modify($request['dn'],$update_array); + + if ($result) { + foreach ($update_array as $attr => $junk) + $redirect_url .= sprintf('&modified_attrs[]=%s',$attr); + + header("Location: $redirect_url"); + die(); + } +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/delete_form.php b/lam/templates/3rdParty/pla/htdocs/delete_form.php new file mode 100644 index 00000000..984f5bfe --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/delete_form.php @@ -0,0 +1,157 @@ +getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setDN($request['dn']); +$request['page']->accept(); +$request['template'] = $request['page']->getTemplate(); + +# Check if the entry exists. +if (! $request['dn'] || ! $app['server']->dnExists($request['dn'])) + system_message(array( + 'title'=>_('Entry does not exist'), + 'body'=>sprintf('%s (%s)',_('The entry does not exist'),$request['dn']), + 'type'=>'error'),'index.php'); + +# We search all children, not only the visible children in the tree +$request['children'] = $app['server']->getContainerContents($request['dn'],null,0,'(objectClass=*)',LDAP_DEREF_NEVER); + +printf('

%s %s

',_('Delete'),htmlspecialchars(get_rdn($request['dn']))); +printf('

%s: %s

', + _('DN'),$request['dn']); +echo "\n"; + +echo '
'; + +if (count($request['children'])) { + printf('%s

',_('Permanently delete all children also?')); + + $search['href'] = htmlspecialchars(sprintf('cmd.php?cmd=query_engine&server_id=%s&filter=%s&base=%s&scope=sub&query=none&format=list', + $app['server']->getIndex(),rawurlencode('objectClass=*'),rawurlencode($request['dn']))); + + $query = array(); + $query['base'] = $request['dn']; + $query['scope'] = 'sub'; + $query['attrs'] = array('dn'); + $query['size_limit'] = 0; + $query['deref'] = LDAP_DEREF_NEVER; + $request['search'] = $app['server']->query($query,null); + + echo ''; + echo ''; + echo ''; + + echo ''; + + printf('', + sprintf(_('phpLDAPadmin can recursively delete this entry and all %s of its children. See below for a list of all the entries that this action will delete. Do you want to do this?'),count($request['search']))); + + echo ''; + + printf('', + _('Note: this is potentially very dangerous and you do this at your own risk. This operation cannot be undone. Take into consideration aliases, referrals, and other things that may cause problems.')); + echo "\n"; + + echo ''; + echo ''; + + echo ''; + echo ''; + echo "\n"; + + echo '
'; + printf(_('This entry is the root of a sub-tree containing %s entries.'),count($request['search'])); + printf(' (%s)', + $search['href'],_('view entries')); + echo '
 
%s
 
%s
'; + echo '
'; + echo ''; + printf('',$app['server']->getIndex()); + printf('',$request['template']->getDNEncode(false)); + //@todo need to refresh the tree after a delete + printf('', + sprintf(_('Delete all %s objects'),count($request['search'])), + (isAjaxEnabled() ? sprintf('onclick="return ajSUBMIT(\'BODY\',document.getElementById(\'delete_form\'),\'%s\');"',_('Deleting Object(s)')) : '')); + echo '
'; + echo '
'; + echo '
'; + echo ''; + printf('',$app['server']->getIndex()); + printf('',$request['template']->getDNEncode(false)); + printf('', + _('Cancel'), + (isAjaxEnabled() ? sprintf('onclick="return ajDISPLAY(\'BODY\',\'cmd=template_engine&server_id=%s&dn=%s\',\'%s\');"',$app['server']->getIndex(),$request['template']->getDNEncode(),_('Retrieving DN')) : '')); + echo '
'; + echo '
'; + echo "\n"; + + echo '

'; + echo _('List of entries to be deleted:'); + echo '
'; + + $i = 0; + printf(''; + echo "\n"; + +} else { + echo ''; + + printf('',_('Are you sure you want to permanently delete this object?')); + echo ''; + + printf('',_('Server'),$app['server']->getName()); + printf('', + _('DN'),_('DN'),$request['dn']); + echo ''; + echo "\n"; + + echo ''; + echo ''; + echo ''; + echo ''; + echo '
%s
 
%s:%s
%s%s
 
'; + echo '
'; + echo ''; + printf('',$app['server']->getIndex()); + printf('',$request['template']->getDNEncode(false)); + //@todo need to refresh the tree after a delete + printf('', + _('Delete'), + (isAjaxEnabled() ? sprintf('onclick="return ajSUBMIT(\'BODY\',document.getElementById(\'delete_form\'),\'%s\');"',_('Deleting Object(s)')) : '')); + echo '
'; + + echo '
'; + + echo '
'; + echo ''; + printf('',$app['server']->getIndex()); + printf('',$request['template']->getDNEncode(false)); + printf('', + _('Cancel'), + (isAjaxEnabled() ? sprintf('onclick="return ajDISPLAY(\'BODY\',\'cmd=template_engine&server_id=%s&dn=%s\',\'%s\');"',$app['server']->getIndex(),$request['template']->getDNEncode(),_('Retrieving DN')) : '')); + echo '
'; + + echo '
'; + echo "\n"; +} + +echo '
'; +echo '
'; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/download_binary_attr.php b/lam/templates/3rdParty/pla/htdocs/download_binary_attr.php new file mode 100644 index 00000000..251f2deb --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/download_binary_attr.php @@ -0,0 +1,49 @@ +dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$search = $app['server']->getDNAttrValues($request['dn'],null,LDAP_DEREF_NEVER,array($request['attr'])); + +# Dump the binary data to the browser +$obStatus = ob_get_status(); +if (isset($obStatus['type']) && $obStatus['type'] && $obStatus['status']) + ob_end_clean(); + +if (! isset($search[$request['attr']][$request['index']])) { + # We cant display an error, but we can set a system message, which will be display on the next page render. + system_message(array( + 'title'=>_('No binary data available'), + 'body'=>sprintf(_('Could not fetch binary data from LDAP server for attribute [%s].'),$request['attr']), + 'type'=>'warn')); + + die(); +} + +header(sprintf('Content-type: %s',$request['type'])); +header(sprintf('Content-disposition: attachment; filename="%s"',$request['filename'])); +header(sprintf('Expires: Mon, 26 Jul 1997 05:00:00 GMT',gmdate('r'))); +header(sprintf('Last-Modified: %s',gmdate('r'))); +echo $search[$request['attr']][$request['index']]; +die(); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/draw_tree_node.php b/lam/templates/3rdParty/pla/htdocs/draw_tree_node.php new file mode 100644 index 00000000..7311e2dc --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/draw_tree_node.php @@ -0,0 +1,61 @@ +getEntry($request['dn']); + + if (! $dnentry) { + $tree->addEntry($request['dn']); + $dnentry = $tree->getEntry($request['dn']); + $treesave = true; + } + + switch ($request['action']) { + case 0: + $dnentry->close(); + + break; + + case 2: + default: + if ($dnentry->isSizeLimited()) { + $tree->readChildren($request['dn'],true); + + $treesave = true; + } + + $dnentry->open(); + } +} + +if ($treesave) + set_cached_item($app['server']->getIndex(),'tree','null',$tree); + +if ($request['dn']) + echo $tree->draw_children($dnentry,$request['code']); +else + $tree->draw($request['noheader']); + +die(); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/entry_chooser.php b/lam/templates/3rdParty/pla/htdocs/entry_chooser.php new file mode 100644 index 00000000..1bde9ede --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/entry_chooser.php @@ -0,0 +1,123 @@ +'; +printf('

%s

',_('Entry Chooser')); + +echo ''; + +echo ''; +if ($request['container']) { + printf('',_('Server'),$app['server']->getName()); + printf('',_('Looking in'),$request['container']); + echo ''; +} + +# Has the user already begun to descend into a specific server tree? +if (isset($app['server']) && ! is_null($request['container'])) { + + $request['children'] = $app['server']->getContainerContents($request['container'],null,0,'(objectClass=*)',$_SESSION[APPCONFIG]->getValue('deref','tree')); + sort($request['children']); + + foreach ($app['server']->getBaseDN() as $base) { + if (DEBUG_ENABLED) + debug_log('Comparing BaseDN [%s] with container [%s]',64,0,__FILE__,__LINE__,__METHOD__,$base,$request['container']); + + if (! pla_compare_dns($request['container'],$base)) { + $parent_container = false; + $href['up'] = sprintf('entry_chooser.php?form=%s&element=%s&rdn=%s',$request['form'],$request['element'],rawurlencode($request['rdn'])); + break; + + } else { + $parent_container = $app['server']->getContainer($request['container']); + $href['up'] = sprintf('entry_chooser.php?form=%s&element=%s&rdn=%s&server_id=%s&container=%s', + $request['form'],$request['element'],$request['rdn'],$app['server']->getIndex(),rawurlencode($parent_container)); + } + } + + echo ''; + echo ''; + printf('',$href['up'],IMGDIR); + printf('',$href['up'],_('Back Up')); + echo ''; + + if (! count($request['children'])) + printf('',_('no entries')); + + else + foreach ($request['children'] as $dn) { + $href['return'] = sprintf("javascript:returnDN('%s%s')",($request['rdn'] ? sprintf('%s,',$request['rdn']) : ''),str_replace('\\','\\\\',$dn)); + $href['expand'] = sprintf('entry_chooser.php?server_id=%s&form=%s&element=%s&rdn=%s&container=%s', + $app['server']->getIndex(),$request['form'],$request['element'],$request['rdn'],rawurlencode($dn)); + + echo ''; + echo ''; + printf('',$href['expand'],IMGDIR); + + printf('',$href['return'],$dn); + echo ''; + echo "\n\n"; + } + +# Draw the root of the selection tree (ie, list all the servers) +} else { + foreach ($_SESSION[APPCONFIG]->getServerList() as $index => $server) { + if ($server->isLoggedIn(null)) { + printf('',_('Server'),$server->getName()); + foreach ($server->getBaseDN() as $dn) { + if (! $dn) { + printf('',_('Could not determine base DN')); + + } else { + $href['return'] = sprintf("javascript:returnDN('%s%s')",($request['rdn'] ? sprintf('%s,',$request['rdn']) : ''),rawurlencode($dn)); + $href['expand'] = htmlspecialchars(sprintf('entry_chooser.php?server_id=%s&form=%s&element=%s&rdn=%s&container=%s', + $server->getIndex(),$request['form'],$request['element'],$request['rdn'],rawurlencode($dn))); + + echo ''; + echo ''; + printf('',$href['expand'],IMGDIR); + printf('',$href['return'],$dn); + } + } + + echo ''; + } + } +} + +echo '
%s:%s
%s:%s
 
 Up%s...
 (%s)
 Plus%s
%s:%s
 (%s)
 Plus%s
 
'; +echo '
'; + +# Capture the output and put into the body of the page. +$www['body'] = new block(); +$www['body']->SetBody(ob_get_contents()); +$www['page']->block_add('body',$www['body']); +ob_end_clean(); + +# Render the popup. +$www['page']->display(array('CONTROL'=>false,'FOOT'=>false,'HEAD'=>false,'TREE'=>false)); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/expand.php b/lam/templates/3rdParty/pla/htdocs/expand.php new file mode 100644 index 00000000..0eed2422 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/expand.php @@ -0,0 +1,27 @@ +getIndex(),'tree'); +$entry = $tree->getEntry($dn); +$entry->open(); +set_cached_item($app['server']->getIndex(),'tree','null',$tree); + +header(sprintf('Location:index.php?server_id=%s&junk=%s#%s', + $app['server']->getIndex(),random_junk(),htmlid($app['server']->getIndex(),$dn))); +die(); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/export.php b/lam/templates/3rdParty/pla/htdocs/export.php new file mode 100644 index 00000000..ece7edf9 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/export.php @@ -0,0 +1,40 @@ +getIndex(),get_request('exporter_id','REQUEST')); +$request['export'] = $request['exporter']->getTemplate(); +$types = $request['export']->getType(); + +# send the header +if ($request['file']) { + $obStatus = ob_get_status(); + if (isset($obStatus['type']) && $obStatus['type'] && $obStatus['status']) + ob_end_clean(); + + header('Content-type: application/download'); + header(sprintf('Content-Disposition: inline; filename="%s.%s"','export',$types['extension'].($request['export']->isCompressed() ? '.gz' : ''))); + $request['export']->export(); + die(); + +} else { + print '
';
+	$request['export']->export();
+	print '
'; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/export_form.php b/lam/templates/3rdParty/pla/htdocs/export_form.php new file mode 100644 index 00000000..326184e7 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/export_form.php @@ -0,0 +1,213 @@ + 'Macintosh', + 'unix' => 'UNIX (Linux, BSD)', + 'win' => 'Windows' +); + +$available_scopes = array( + 'base' => _('Base (base dn only)'), + 'one' => _('One (one level beneath base)'), + 'sub' => _('Sub (entire subtree)') +); + +$request['page'] = new PageRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->drawTitle(sprintf('%s',_('Export'))); + +printf('',JSDIR); +printf('',JSDIR); + +echo '
'; +echo '
'; +echo '
'; +echo ''; +printf('',$app['server']->getIndex()); + +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; +echo ''; + +echo ''; + +printf('', + htmlspecialchars(_('Proceed >>'))); + +echo '
'; + +echo '
'; +printf('%s',_('Export')); + +echo ''; +printf('',_('Server'),$app['server']->getName()); + +echo ''; +printf('',_('Base DN')); +echo ''; +echo ''; + +echo ''; +printf('',_('Search Scope')); + +echo ''; + +echo ''; + +printf('', + _('Search Filter'),htmlspecialchars($request['filter'])); + +printf('', + _('Show Attributtes'),htmlspecialchars($request['attr'])); + +printf('', + $request['sys_attr'] ? 'checked="checked" ' : '',_('Include system attributes')); + +printf('', + _('Save as file')); + +printf('', + _('Compress')); + +echo '
%s%s
%s'; +printf(' ',htmlspecialchars($request['dn'])); +draw_chooser_link('export_form','dn'); +echo '
%s'; + +foreach ($available_scopes as $id => $desc) + printf('
', + htmlspecialchars($id),$id,($id == $request['scope']) ? 'checked="checked"' : '', + htmlspecialchars($id),$desc); + +echo '
%s
%s
 
 
 
'; +echo '
'; +echo '
'; + +echo ''; +echo ''; + +echo ''; + +echo ''; +echo '
'; +echo '
'; + +printf('%s',_('Export format')); + +foreach (Exporter::types() as $index => $exporter) { + printf('', + htmlspecialchars($exporter['type']),htmlspecialchars($exporter['type']),($exporter['type'] === $request['exporter_id']) ? ' checked="checked"' : ''); + + printf('
', + htmlspecialchars($exporter['type']),$exporter['type']); +} + +echo '
'; +echo '
'; +echo '
'; + +printf('%s',_('Line ends')); +foreach ($available_formats as $id => $desc) + printf('
', + htmlspecialchars($id),htmlspecialchars($id),($request['format']==$id) ? ' checked="checked"' : '', + htmlspecialchars($id),$desc); + +echo '
'; +echo '
'; +echo '
'; + +echo '
'; +echo '
'; + +/** + * Helper function for fetching the line end format. + * + * @return String 'win', 'unix', or 'mac' based on the user's browser.. + */ +function get_line_end_format() { + if (is_browser('win')) + return 'win'; + elseif (is_browser('unix')) + return 'unix'; + elseif (is_browser('mac')) + return 'mac'; + else + return 'unix'; +} + +/** + * Gets the USER_AGENT string from the $_SERVER array, all in lower case in + * an E_NOTICE safe manner. + * + * @return string|false The user agent string as reported by the browser. + */ +function get_user_agent_string() { + if (isset($_SERVER['HTTP_USER_AGENT'])) + return strtolower($_SERVER['HTTP_USER_AGENT']); + else + return ''; +} + +/** + * Determine the OS for the browser + */ +function is_browser($type) { + $agents = array(); + + $agents['unix'] = array( + 'sunos','sunos 4','sunos 5', + 'i86', + 'irix','irix 5','irix 6','irix6', + 'hp-ux','09.','10.', + 'aix','aix 1','aix 2','aix 3','aix 4', + 'inux', + 'sco', + 'unix_sv','unix_system_v','ncr','reliant','dec','osf1', + 'dec_alpha','alphaserver','ultrix','alphastation', + 'sinix', + 'freebsd','bsd', + 'x11','vax','openvms' + ); + + $agents['win'] = array( + 'win','win95','windows 95', + 'win16','windows 3.1','windows 16-bit','windows','win31','win16','winme', + 'win2k','winxp', + 'win98','windows 98','win9x', + 'winnt','windows nt','win32', + '32bit' + ); + + $agents['mac'] = array( + 'mac','68000','ppc','powerpc' + ); + + if (isset($agents[$type])) + return in_array(get_user_agent_string(),$agents[$type]); + else + return false; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/images/ajax-progress.gif b/lam/templates/3rdParty/pla/htdocs/images/ajax-progress.gif new file mode 100644 index 0000000000000000000000000000000000000000..994bfab65524de391b10d7e2c9fc1115a154b43a GIT binary patch literal 7685 zcmeHMcU;rg7XKv#31OHZG88lfWQjHcB3MV50t$v9qT+k3MntWs;IplLFHD&&OBsR? zHW6r8Dq$rMFpOZ>0YO>HrUBXZJzt3WAtF^Q{@LWurE6C-UMhsyvI zm>UHi7B*B@vBpLwS?w><(kmmQ$o>5jQ**dX zr?w9Ek1-m%Uk*)$UDVlCC%d_?3kF+yRjwY5@r>x{T`uod~#}Va3VIgC^5cl zWQ2V?rZ_s9e2-8Tmq3Y+DII4|my(+&MqU+>8)KtNVq(Z6Lz9uwk4j2gYwDiXGP=r3 zo9@OG$3zv8OPXS1ifGmC6O%KwjAwNk{DNZCx#q* zxA^yQc3Ej_d3iI1(pFjB79Us49-l5MdQwu{+}7ToM4+Z5Q;6ABsVOvi9jm8z_+i15 zq~x;P-1?rr(Y%5>I=yp#;dM$9t*QC>)YN=+HLI?sv#V>Ufze%C(~+7+OCwgj9Q=J| z<~1RK!en$O5Gwkf59egp&CSiv&Ay(Qn@vupr4wn%sg(O!)zj0nU0s81Z7&w)77Oxf z8H}#vBx+h(MHaE9spWZn?NegbhLU z{QSTuptiK_QknozbD&vJ#+Z~y^@eUzwWE^=mEI#L(>UYYq^ck(4c9JuZoh%&HvK>u zle~e)?uu4KI}tNMMDq+57qNlnqI&qewh8ZcCryGW5gOhs|7-e5&|C8Q;Redn zxAc7ZVR$y%Oz%4LdRi01{NN&VR!Sm@fPnrE-@P|<$xjsYyxymXeqm4-y??C_D znI@JA1b0-{p7;r`#A}N5b_7esB5V$h@o2Gi-oey9U4^%_f z5`ukbjCI3lC5PwXSG@I)gvIT&u5j@*?9mh0`VDYEN5elvd!Is}o3^@wiI3iYl&x(O z0RiOU)|)kC@p_w7HS@m`oenp;H^5W$o(fp!?u&IfA;~yZIK;?L>*pWuj7sb_zY=S2 zVQC+-&7eaKfz0C*6xsX3?1B9_MQ@mhydH_Y#}Mx?wFBdZ(4^uOFY_tK>!)V5$Os=T zGuDvIVW}SLBw|dRln~LoJn)&F=ziKpAbg9CSNMq8hx85YORds6-eB&rb#~-r8i1xB zQ$3d*LAqS?h)PH{ZrH5qP(VSs5xherfqQY%G^4n!Vuso>(%}kvgu~*hYFK#YufFGV zWB2s$4V1}s`Ij3oWyOffI!e8GqP7phhz9%28(i4|gKpfoPt2WqO&Q2g=gknv$4O`K z4wzO!8D-MRotBrPr5yM#Nj~_lz1l)!GZZYZfA`SAIu9YqbZs-z8Ym1EOW7+VEvdUn z*kwu!-=t$*Ys#RK27)xN^#*2Fq-4WVG_Q+!c328T6Gb%4{{WDgVe-nE&mdQj@`m1< z^|GMz_(TCd0$*dRx?~J zqQ%991#l|jU%B5AaV8fe)OUL+N-+m60jbwAB5%ifJJd*?e_65ljIYU=&Bo`)6u|kZ zqUaM_adGn?jHUb$EKbbeWL>oQH@hDEaP>OzDd}XnIzL_)XQ^``-sk6|)gq#@hF5d2 zg30_6o|h({j5|v*TW;>I>pJj*SyNz(9KJH>aY!I*5@~g0Xw} zp_2i9rsD8z+rC#A@*2eA@yo>w!wGg5H`t7&VAo6neygTf-{7D`!3~-}Y{d-AsD79F z(%yE;Ft*68%EqC|hTn3_w*i!knGtzwJXbTpwFM%^b5ch+&ZD@_{n$l%vHg7 zwd05iL9bdbA=jK%B@=H~f0|=@6W8=ku4%V-rY%VX%<25lE1VY5LEpVF*krP)!jby6 z1(D;?I_Eg+H*nVry>&g2>IniZpUaXs1983p6auvKLzaJ7UbqeTsSwrb7l3$c4|H~y zlVwQ(l;8Yh${`6}U+p-y5L{cw-!HLYUUxWi{gTXHD^Dcz4>1~0v{*?-pCC&Ua4@sD*l{TD^$)Zpsd1fzGJO^(n|jZR>A8N zi3|T2O1{tp*D~j`Fx-7h2_=_maU@MCaHj?4#*If|NFLT`apKv>jc4%-<5{WN3UG4$ zC>Jk~*|QDU{kKd{W$H-XxfzDrZI^o@y5NLoWVJa8eP#r(Ds-q0*bXS{y$adUdKW3` zoZ>>#^WXfI*p7FV&ZUz@ewx{71xPt7-p+3`VLf#2>S0E?J%qpK1e`W*#DGsA4XKRn2%=^f#T4| zh)bV0>!i;+yf_n|^9;ReLneeTwnQY+UG#RIQ0z&<;jaVPx^j@Y9ky9QnJuLGnah-C%p&`z)24TRnmiX7>Feir)V1~ zZksG%p>-7EKZeoMH@kQ6*I#0467?$m;c7RJA&@8|LHNkk5$k>Hn&+`H^Vc=c!|Y36 z$2@NXose8e#xHk>OVGS+5;_4sU3+Jj=Jaz#-}m;3rwdB#;BOn!jbw;{P~V0!u@UE< zBs<~MKNz+9kU4YxhA*Dhc}Lr~eqOEgb%iQd^wL8W5U~FeunwMHq&>fXPOSjg?-W|3 zdip>xK1Ycd?NEII=V;zec`4lY3Z`F^nv_D>a?n5T6bj=kAhyMIYU~I1`ePe5Z!z+* z?{x|U5PMAV*duuhFZ0mwnAI6K&_Ur;(HGkhAsK@?w6t$zSFuHxWZuY7MT z-s|RU91ivUA2x%8p*BT)T4TO1FC}|<%gd7UZxzf>pL(EZIp0OU^5+E1vhBKDnU;vdklm>+(k|9tJxV-7j70)9I zf!50!{B7eB9BKPbR=*FqK6s@GQEukS0fwBX1HE|8v(w?FHh`e-u{7Rh!wE}u*!sZA TRS@nf!FPIzm+~vs(zpKy%_B!@ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/ajax-spinner.gif b/lam/templates/3rdParty/pla/htdocs/images/ajax-spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..1ed786f2ece49ec5db07dee13a56ef38025b628c GIT binary patch literal 2037 zcmY*ZcTf|19{+AO8xjIZfItFCFrkL3BodGwflvety+|>T03uy{D35o<4X`9Q3=bSU zojZMs3Qw_)j=f_!)B!!mUKqwc>ezd^4c;H{$Ifr|uTTHRC8&buXgI)uM*u&6{`~Rd zM}L3+_wV1IK7G1x-@ebEKY#i1<^B8j-@bj@wr$&s7cWknII(;8?sMnPJ$(4^-Me>t z_wN1p@#D#pCr3v|A3b`6qUeJM5ANT;KRi7A^5x65YuBDSb?WNXs}mCwVPRo|gM+(v z?RxX(&Gzlv_w3no`}XavTesf4dGq@9>xT~?_VDnybm`KK8#fLfJh*P%x(gRB?AWp6 z>({T(pFdAaOMCY0SzBA%hYueT6BF;;xnpHzb@}q;O`A4(d3im4{J5Z?;ONn#0|NsW zFJ9cgfB);(ugAv5a&vP_OG`&aMz(C(^6J&A@$vBk2M!!Re*DRkCvV@rJ%9duQ&ZFC z&6|%MJC>4?a{Bb?vuDqqIdg`~z*4Ws1>(;I2=4ORL zQCC-|R4OYgD_5;rwQSk44I4IW+_=%t&(GD>Rj=1yxpHM_Xh`ytnG&0k9<5Zz%KT@c z2mnYvQ>hsF`jQ_R5(mKIydH2saldkg!Gzlvi9nFeu<gyfnCs8|SGO1R0M3N~Sp zx@MoynP5Rh(303ldPHFemMT%$+lXy}A1JR?IwL6=E`apW=@Z-0R!QO^@j_&{6#;i|5R1o$ zJ1J~xCDQ$1cs9`DW9Z!)D)^+%&Ug81908UkMXX;jkp>#b+SE}d{`0S>>Ef(`Ns6oa zB~H-LX25y1!BDgW%?nC0$RettYz8zsuz>9Z!g8TH$kU;rwYWrSTkjuYCH9utgFU6b z*wzBKaG6IjEXYSpAo5j4&c(t zwi-c(Q6YX2N-v2q(mgN`>wuCTV&XSRUA4h-f8g+uV2k_=o;2wb`7N)&+7T-C+CT_Bu4KrjWmK>x0AbH2rmR&7+q6fX+R0o&}V!t?TP+g0{x`8PSP{7CvnEPL^Hzx^T2Eus2 zi$U6ZM7}+l%$}afWn#%|+MPTsC236GKi9-ad*Z9;Ymg?jSO$L1s$w4BvDzu_kH9>z zTWC{K?jx4~H3oGGt3}I}LWNw@H)C>9GF4^|x(5n#+Va}kr_cb>{a+K%>B+@JNrC8? zJOUkD3fe*NdA-rJupqo?FfRK}k zOm!l7Dj$dsL|kS`3FL3^@^7axrU9d*@79yf!+bu3kXbziU!v&TO3p#w{y_lYG5ZPzDh;giB_lkGp((nqZ12&%LTrgm4vY= z{ffd0B$DiUKReJ9>?{#_KVrMyhiu_A8fNd!&DWTZ4+9S|1lJHkLv-^}a0IC{WI9N! zQTY-}db!%OH^;l2ZeajnA&Q6Uv=#0KZB9;^^lzMOi^0~bS~m!&K#e6hia79(ItV9M SXP~*+AU!zMlD%~Wg#Hic*k=_0 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/af.png b/lam/templates/3rdParty/pla/htdocs/images/countries/af.png new file mode 100644 index 0000000000000000000000000000000000000000..cda88b08594d89da861bd1773275d8bde166cc6b GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzk22B@ngr8 zl$Aq5L$6%Dx?|@~hG+%`O9loD2G#^tfh++*Z$a4-Ss@Q0#Y#oVP)Yq3ebX*ei%v_6 z77M2-PDc4gZZqACa*Q0i9IdLXJ*RsH%n$IN>mM*XAbfFn@XX+#=|SQ1!lM^Q$1RRa zU6z`)HY;&nV(!{pc?J3Ti|2p-@%hEu7uRlIyLA21mmgnVym@iv!kKSBzePqxUbuKc zAcI#rN5Z<^Tp*5BK25-+P+mVwYlC=&Z0G`riT_Kc9sY2TvBaqX=-Gszi-bD12qv}TgB8s zoyiq$YkGnvR|jmLle2AZ{`Q3xS9bS3Jw1ER@}}dPdfwgKm>%c1Z{5Tv*AG0sb@afT z_}#P8b}cBY%nH1@f6A*%s~?uU9Pft%vOG`sTLs3ytR#sM8T3SLvf}fwCmzS4^hli7s zla-Z~nVA_Fo4`071w@8G`xHAlCQOcs``ORr9K zJ*#WEqmrdXHKoxhX3wLnRYBMM&h@3$I3*Q*a!{=EkMw$_$ruNIVK6?I)OVcW!w z3-@iGp1YuZ|L(@Vb#eh6GdFJ9r)aEbzh1+9`I=437&Sto-krK8B0P7-JgNC1foHGq zh^$(box|zb*>kqzwBKse(qwk7nLB)DS=nAOzoV|uy>;#4)#4ggEK}7EpE+c(zhlLd zrg!@|g&Uosj3v^1Kl9|}>uU17dYL}6=d3lu#7zd)C(m`v28DsAtDnm{r-UW|r0YS$ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/al.png b/lam/templates/3rdParty/pla/htdocs/images/countries/al.png new file mode 100644 index 0000000000000000000000000000000000000000..5925b279c5a3aabb86b23c877b7197384bc4e780 GIT binary patch literal 1040 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFziMiB+{*fY zdImcq!x;vK6ATRV85rsr7|IwJVi*`C7#KD(GOl4{Jjcv@hM75pkD=$}6+^ME^*Fb-dhDM=;#3xhJ{aRW_bafBv=)>$LN zhZYuB%}g(un4C8@{%&V?*TSMwUfxYe=$DhzEi0?tMn(}563-nS9@yLcadUg+7SR^6DOx;HMLi6Zbz-H|NHvB^Ynb<;ql+!|DV7ADpS)L`uhI@0zUcp{0$8J6Auv!OH5Ao7=muk(&BHE9*yA*4xz76&@bj0|P_U)TDWN z%dM=o#Ki?@Yg;KQ>dDGVh=>RX2(YrUGBYy+BMumSqkxhSXyGpS1k}%%;hk_iPq8xK=m)2$7IrPcx8Dr;82SwNI zvzC2x3srP1jaHw(V@}0_38{?Uo~{2=!lrXxY@PTrbD@LcvuQ=~!m9H;g?~$R1sN;v zX>dAwRgtYIMkw~L=GLz}4Sm)*EZoHs9BCK#^1;1Tn;!jeD0g2Fuy1P@!|o}5>E&ha zVJo&biw2xFiL~FTplp2V^vz>D8cWnq9@Af(D>rY7^y8OV`H4Metr_lr)@#|J RcGd|LES|1@F6*2UngEvD2oV4P literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/am.png b/lam/templates/3rdParty/pla/htdocs/images/countries/am.png new file mode 100644 index 0000000000000000000000000000000000000000..45cd7495779aecb815fb80f7494da52acfa230ae GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKgh^?fRXnE zm)K!mi54!QOA;#Q)Q#?Io2}9^dTZzT)i?Z!XYeDBppSvk4~JAG`(GeeWvpzF0oMC1@$I5zvmGulK z=UHCfYXSl{BqT1$%05(7d?K%KRbT&|sp%&JgHsk356w)knwVU2a{A@w_Q%ojnUm8~ zYpdgKZm<0P{|5y8^Yi-_5b!fR{99byue8+fSy}(jR4|_@W4}s1%KNZaBZg7=k?*AH^seJ9dn>q;An-|ks5{N z+4B4A^{!5|`nAU6)gq5qOT7PY3H`Y-^u@}EGYky%3=HR(nL)l~Wj)Kud5xF%hJe5& z35h9!f)8b7pC~F`k(ZwX#eGjr{SWl#|Np?q9R>J9V0T)|DxeC+ByV>Yh7ML)4Fb0^Pk zpD(VVqPTvAf~J;^ijtb1s*w#Cw`NF6&W^rm zRsE~N;udo&&)2J=Q>TVpG1*ee`=w-yNu!f$?1CGltyREt6IOkx%-WLJ8p9k!C6tw$k-2SHt2cKsgeHFd<(5eF`SM5J>?fE;V z#!Z6#2ZTDdiFIvPm~&cw!XB-K=hgbR>&!T0IDMab`831HI}Ijo)9+qyyYa61{NpCm z_nJ-J>A3Qu-GXCIE6!U_+TynCwBPpoLHl3$ZoJ{Q>1NQjJHgxUMjUt^zWYJ=&ig)# z4u|i!n|AO?*74VQC*KsEep7hz_5MpY7i`$JdiTMt$Ie`P@&4$wJC`57yz%Vy%kO_4 ze)xLp`RnIjeq4Y2{MEN#3)ieacKrCJ&6}@Wy>j*Hl^e$nJimVF+tY`4T~y9Gsyq(0 z_?aL4z99U;*^`H57!D}29#iKys=|3do%6Vf#1<>H_i;|&;@z%CIDEr%#{W zy?gic>C-1qo;-Z`@WzcB*REZ=bm`KC3m49vJ9qZ%*)wO(oH}*t`0?ZW_wV1cXV324 zyEkmuuzdOQxpU`EnKGrmz8)Blz}OrGB!xiVtLTS74U9?N?k)@+tg;?J&VEl9$B>F! z$q5IT($35fI~ox*Ve-V-+ZV6J#Ip;{vfH^*(N0s_cJqq8OB*)|Y8(-iloLD>6Bst@ zw1|X|j2M^5rQ(kTqCwBa#GYI<+EiGwfr%?}mvyHHN5Lzp&sNo*kbsO;IYHV4Jj{4!P7Yo}Jdk`nvRU>`5PAfP%t zKE6CAsj|+?Df@bhXK;g);oe%?xtS*`TUQ?4_*Z!G@}$pv3@>&Ydatr-NB{+dr>mdK II;Vst07svX+W-In literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ao.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ao.png new file mode 100644 index 0000000000000000000000000000000000000000..9463d243050a0294dd54f0ad4c8c76bedd92734a GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-)Ck0KRv_V z)J#cPSzAX(UO}Fnn^9PdOG%Y6EZjFEM>``&qN&v`Cs%FiG%q0`hWdK;hzOCcE~`ls zolHy=Jv~js!kki4{6s_qtgKAK!vnp&-GYMyjEoEo4D{{o?K)eS>&o@du4jnx;T7R$ zxH6aPOee$prKT_1)ovGvoDF3>CcfhliV+o0F51m6er+g@uWUiLtS<0Wj?J_4V}h zbai#Lw6rudG}P7A)zs7!6%}P=Wu>L1B_t$-goJo`c{w>bSy@?`nVEr635?KDKzsuTLLtvgTe~DWM4f@aPG5 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ar.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ar.png new file mode 100644 index 0000000000000000000000000000000000000000..bd7338260065431f8d086d8035843360ee82791f GIT binary patch literal 1043 zcmeH^`%6=C7{-rERF;*cKNwk*WZ4BPMH94iDs$y!%8ZP>bklC$qO>4#CfyiTm=H#@ zrJEXU(=9X2VjR(0;1OKNG{ASa)DoN**+e; zzhU@5(`c|jagYZNw- z73^?UBVB5Wu9Yx=<@n1F>4T<(Cg~YqVRU2Ex83wsG)0Ug3&1(KJw*Ug__B-R&Ws-H z1gA5+l$G9wGQ?w<1BT3geXhcORprd=p3K(}`5OERYCZK{dR<}5l^M^7M{>F}H?+hJ zEnYlsD}d)NNtCzM_7a7)aA+ojExxB-&Z_Sy)2tNtY4ZgAk1(P_y;S&Vgry_Ok@*VL zbXPj{REJk$jw+ptrLt72%q(QKM(;BJUYa({B#Pj~7LY0$R%x|kC<;bKCZ?wB4hJfi z0~l6n)a{+kUk4<7;S0?~pY;2u){ljaJYQznb{_ZOo7yx0WCp`e0EQk^tmf37(qXFE z8U4!gGLGX;r_*k?+iW(g)oQU=j7Fngug5S9K@b>*0r@GDk!_Nl{-1wLAhOD_kvw%f zf07ypJznLXKteAqE{1VieIcjEoy%KtZ4U7N=);|6+yqgvOiJC=s&rG@%)5YY_Bj}H7ijmgYd;uGtc;Rpa zCb2j87}=v)5%u-&-|_O?b^Sq!ADEQ0*XOsrPWP%@(jM^TM~2)fr$m^`OTvUPsWiU~ z)$HI}QS{Dr=``=P@m?2J@5nB_77`G}c`mG~=8N0jQHw4!SzmZh+`<+i2!Rhh3lwds P`x^o+J~>WE$+`arQpLFE literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/at.png b/lam/templates/3rdParty/pla/htdocs/images/countries/at.png new file mode 100644 index 0000000000000000000000000000000000000000..2a58c49b1ed44161fdd633e81adb53af479bc081 GIT binary patch literal 1041 zcmeHE>rYZ~0R7n=EiIPzNfReh?N_9Lr^2QKJrn`T4kZJK2%$-r@2+yTDfYu z`JrsHTxC|PS!v~q?ghjv7mAq=P!thOOx|8ByZsA&?ri6roe$^BIdzCsOb^)@0sx@r zWDD~t-#~HQ8i>*oE%O!t5I`h_SyWtES;4o&YL#CsBcsg^n@Eor)o6Sqx#)JAEtV0J z$zU|<42D6y9w&$aolY|_pz80(G@8J#U*z(#$LDif=<`b>4wly zJUo0LJiIsci%gkcwH%F2(l=ZdJv>tAZSZZcNG;4?U6W( zi@U|5h0;<_Sy{hSN*+Brd+^{)S=q?G(j~dvRbK9s%LA2_4!PWa@}xtduqqV(swyLn zW8K|oSJx0hd^Z?0N~OB5Pu1It_4F*cTz;o>!ESfV%*@+tqei0v$JI(D>G8~3tuvF8 z*75Pr9UWe$^BY0vv|8#x>c{{1hXuAfTb@$2pu@YxLQqR%W>cXnN0?byHGJey_bLu& zenZl!8j1VsnVZRMEmN>VbOHkM2y3O9~I<7X>FM zd9U9+ezEsM#hu)hd%*>PIOyuLHxPq4T65Qx`#foP$f<4B@RcXzYzJCo%6nR|_0gpV tcn0ePv^ROz>4sNIw?Fi9)Ym zjG1S=12*Yd)+uW2GO;T+u{@Pour)M!!{ikaRssF#-ILP$&uuwV>{vdtdfxeMw~A8Q zHY`05=h1d(%k70zcSQu}v{f8TiE9gXpV8NLC?{oEan7pwGcSp7*6XPB73Ci2XgRrS z=P^O{!3J*`7aD|7vO+=_uRHR>CZi1r1G*y+^`g&8{-Bvp}&JXb4Z(dRds^Ae3_2UVgrNa`UZ;( z4VN1lE>l&lRa37wHC?Tx)ox+A*2rj%wsxzP)p`SiUVDeFCZQw zkB>hT9I`kg<9K?;k=)!9r%s*b5$IA<>$b6*6`y$6*k$$W(xM}Nejiejf260c^Y@$L z?e(I%dUtmA^WGj{%mU+g6yOPgl?gUVKxxJ#Z+91l4pvzYAm^Z`i(^Q|t>lCQOckn2 zmv?jD@Vi=8aBaHonf2^kh3Dp-K4aA19ewtmcEHUgn;12Y*kzwPletDFr)ynZ;>o2^ zS`LbWat|dJIVDZLQ=_!`)GL$R2B#XSARnWHf0}kZ*F=W~#7gVW8bqE)u@ z7&}$Zn$8uPwlUCQp@O)qbNv4qOq;)TDZSqELLlcJBg4;yMv^i1*EWJY?&<31vd$@? F2>_&xHE{p{ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/aw.png b/lam/templates/3rdParty/pla/htdocs/images/countries/aw.png new file mode 100644 index 0000000000000000000000000000000000000000..411977eb78646383292f19ff9cbb0af162fc77f6 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFU*+ihtFifc zcYkM0d|g1;-uWv}uHTy&o4NbQwXLTfdBo&RShh1|!O4szXR;Qb&R=yod&#M~?e{A; zTDl$(-e9vzMOAT5>XL@v*|y7xPw}E?j+~eB<@94Ofd+o-N;awPxGxvNh)` z)?KXIcB^vT`IbEoTlYR}+xM{d=!^biFQ=Y)Km9Dw#*1?^HE-+iog{mwOaUMxI!bM?)qTkpRtUb&@c#pc>gdo%l{)hu1tx^8>zk~Is@ zUfcNK)!JK+OIL30+%O9y3uc|Y-n-+#ikpunY_C{$a#H)otnRIOU7K^K?yFdI zs;z!bcGrrMO*f`Z*<3q!Z`=MSOV^#BcI5f0z4w<++~PNBi~qEpAv1SIEj*I2{$fGj zinvuL3TJFi+jqZl(cb)xXKPNq>^uEx(&76(hwk<5HZpn{8TqTQ5E!|@C>{m4LLh>< z?FUefG0EHAg`tC0)&s~n=;`7ZQgJIe;Q-U2MUj<@pUC*Mgk1fqb0l_8qmxZeapVrA zvs*+$N_^MK)-pFZ&GGsCY_t1|6)Ur(BX3r-RWi07YMyDC#4}~836E~>-M@eCHaN*G zymRi@tgctTo|*aCHLfdjP^`QuAjoJ}=a?AinJKBsX}BPuEXFITCdVnLD9bG@Evkw! zAmiK=&f2|yh1Gp$TV{Xjc^RzE-1;g@`nB||S+}irSMRdA6&>B+B$~R@vhr!Dsp`_B hS6f}D{=Bh=nZfP8;h%^g_xB)Qd%F6$taD0e0sv9vu517R literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/az.png b/lam/templates/3rdParty/pla/htdocs/images/countries/az.png new file mode 100644 index 0000000000000000000000000000000000000000..ced5af010f8fbd23e0dbe18e5b6513cab8c0823b GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF|Mm5oq2)eX z=bh4O8UDCP;1;kTz<-{<-)z5tIRR#MrhcuKwgrj_UG^Fw4BBBVLB%EolLIXhgk6%= zgUSuOvvuQ}>~kmh>W8xDP4p_B68P!>dj@gJ`c9lKr zp>*C`{alE_y(GI`K_$cl!ly2 z^LbYl|FtFkb7$_asa5Coc@J32o$}J3?V$4{-TOw0_oY;y_azZeiX#ukdp|CVJ&_Ug zsk`Leo=pccqYw3U0Am&yzoP(82+TCjaso;-CV9KNFm$lWdH^{GJY5_^DsCkw9AI*B z5i$8vI%Vn6)}u?FOu4ef!O3V<)vi~cgbpp5w5dyL)-4A`K0QW9JFzmwFtaqjJh{5K z1s9e?Zcg8JeujDZp(C1;mzVm_=MvFcyZ-D+A3vXy8J-zB>vT@CbAA0LlDc$z+1Kr7 z6JNd7d4GbL>uPA~@}*`frc*=BuZM_;Jz;7+q;%N1DVdqM>CvO)Mb3+r8l2XNsj(e< z7Um~+Z{NJUb9~zz6tjb!m5sft%jNC$>&4xzrE~5vG6e8wmBtC$?QB-*uJi~b3KDzIfGsqgNzrW zemS#2C9^>Vt5G$pQ6;-c4X0@}uT>+jMJ+lQ%(94^WfnQfDSMrL(p>la4UTCGozoUtNA`H;uJXuT?wB+!pnQ8k*;dDd zNdaY>eGAtFm2YuNn;B5D)+x3lu3<+?+un@M{b_Bxvbqn%l+LT1cBFpx$?B;G8|R$p zTXk#t`m2*xo^7ACW8v0odrvET86edVAcZed!0*#hu=nesN#+nRyIn<};jK zz<6dM>)Az|*H-f0SS@gSy~w>y5|>s=Jlrb#`hfPQ6Q*BJTRq%s@#B)+pDS+vZ}@+@ z;Pd-N;PlCQOfz_de;U1B=Jz6N{_W^F>F?P@gaSWzbD!3F`EJH#B(2gT(NDzyFh4^FxC=;Y<>C|TVGC&zz$ zQg5OZ@2q?(YMd3F#LV$aZnci3n?rkL`|*Y%D37CnV@CB^jis5Nc*xWNlU8?^l(Pz921i zZh(JfRAg&He0OHXWCjL51_oaS215o017_wxX667^)&NeMY3TrU^(;ljaDDwsMa2*`wO9>}1XI&m1A_uBtt4IDG(Eix1A}Y> zgDfqrSQC>%3yVq3dr-LO#W;Tek` z51l^WbxVqrDa=s(_uT*CdAENr0=_>C`1~N?&MD8+yIi^(^q*f1e0MwK)%B>UT~>~^ zvhk7H6}j5Lm<7h~D8Lf}KL6jh0;L&~yxmm%N1H4AFrC0`OPtL zK6C0!fVjqzl^VLo@87y;ocJlkuXr*`Zq7YM2G2;tTc=MvD*}1k)78&qol`;+0GNj! AE&u=k literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bd.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bd.png new file mode 100644 index 0000000000000000000000000000000000000000..56bba0b9d316e9039dfb2e07efadf45753e0bb8c GIT binary patch literal 1040 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFm-kXoa#Ggz z)v=1SX3%F~&}CqdVPH03X4YqBHDYBoWaTvB(@FZ1`z^$aKv@G121EA|U44-6;`a7%LwCdG<*(>Dq+G>bH~Pqwp9vU5yzNNY`XNptkh^^kRtFbvb?)o10@VerXxlC>6y zF7}Ve_mMZ}&Cm6V@zHlsWXv{~?+mia)ssk(=ggL3UYZivWvM>TKyivF`=MaBB`OkU zqXSMv`QMEQT*u0=RDkogmFX)lw=)b3#~B%pF)*BEWIV&jc#4_%1T*s)R@PIjtY_HS zuW)f)03u%AbG*El1q99u3EdVExh^7dQ$pgltn7V7#TRO7PqnpQ>g#_pHT`U5_1Vtu zmy^>!FR%Z;zQFhb#@Z;LN(gM5Wg!bRh%w3A-G!lpRn`N@+2`rv7*cU7IpF}4g@sDQ z-dGc-!#8)Z9u?uub#zktHE-R)PS&3*H!j+=>slW$eUz5>Px#XIY16GY2D!!T(hDZc znLItYCfL=}*V}#4#^zXtfD@N)yPED?wPn}Fz1F9%-E-mA5P2eYVoFd<4pRC%$i@82s^W~gqgWM_CaS109$KhGsl_;|YdxvXg;S04x$F<&kGJXHm>f0Z; zOV^!`onbh2hT-^G#$#s~Pn=^ueUACm8RnB`ShwzCJ$;e=_*u?#SGX=-;o7p3bLT#; z3)grr-4M8TTjb_#ksJ3U&R>$a{ZRJ)6U9qc(_t( zU;hHW{tS5fBJ%7xULGFC$4?aBz0-gA&_YCnOG1KISy|H3(x9j)%fLY2(b3_=A+FQM z1TUYHxpPD7`6G)DuiYNq^ZfQP;O)zxFYiMSn#i57*ShR$aW~rSV35PJ^x%u}zAy8l z?q-HR&5xa?t1{16Yk{fGa!dV{)`qwJ9M{+yud_GZ=xDaZ$zrFQ^>!DleV%r^-E9we zIXsH;JL>Q9JU-}jh{uI+pDWP;FH$0I#s$Afi+Y_A^B^VSZc_O3^q8mVF;CK>&sykR zbG6>?X!Ri2bDx{t=|GR?@u4?kf?uRWKTeKHOHIwn%1TX5O-V_Ki;Ihlj0_JC4+seG z@bGYRbF;9pFfuaI($Z2_S65V2l$4Z|kdP1(5)u>?vS)L*q#TX?b1KQ0+d*ma zuGtnYrJ_fc#QZ&d`LlPUQgBt&?3=8*DykxPnY4<%KQ%bj&OP&%?~l=)Pp{rJnjUqH zbWmJ&Y@1$nYGItIZ2yB@hu(-R2ypH;?$+(!vT9pndPc2Zlu^Knor}wjmd}~AY~RB2 zid?rKn}8K}Gar?Bi}EriCg0!i>Pkz-f`IESnLIHyVNOcR>=Z*g4JSG%Zd=K7`R$iA ly*_95G-*9rq_m-ujbV9{zSx@m&3T}Z@O1TaS?83{1OV&nJD&gm literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bf.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bf.png new file mode 100644 index 0000000000000000000000000000000000000000..1019379aaaf013a54b573b5a5e46d6c370bd8de3 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF|C^q{5XHb4 z!^j-N%pA?k8pp~S%gUa>&Kb|io65_Z%qx%~Ae1U3k}V>UB_UfZt5~imUm!0XA+1)a zre3D5l%%W}uc+Uwua>T+k*Q(YVXB?4qm`|tTcE2~q-RiKU^m&$s>{l_!q}qO!lcT? zX|j`5la<>vH>WNqubE!Hb9~+U+&m|HdQ9~2pX2W{)hA$1fZq(iz`20|vjPHU1f;A? z;YsH*sL?knH#Dm;wQsdEYSGrtR^SU^5R7CNi{i8@FmR|a^Xql;X>$nda*LkfA3ZG~ zV_sbT;#93F3CA}5%H_QC4#-}aFL1P;^@6?PHZg`X((EUB8Fuin?w6OiXJvZ9&f>6@ z$tz#CD;`ce?XBO22Rse&dm0=5AwTOl14AtXLk$DN2}Z`_jEtw4nNKh?pJrt}0Ysdf z=eW2o@baGIoPc6MK#oOT!)J+Qa?;pXMDB|LEiMCm`TYVBq(_z;6Kom)+bx1O$8v5C0k$_cSu{M@q`))YRWuS-=Pd zM(ZdbCIn{gm3;$L$C%{p?!wT)D(eB{9Po5;45_%4oN$0?(j@I&Y5v(-=X}nb&|jTD zpFzY+FLSQx-o-ZBhPKPMY+IUd6ri!b@YtDqtL|Kx@@CPiXYV|19Tv{g^a*~=b;+rT zTU**n{o8iN)+C`v%7>hqnf@>zUYzuJk#eI`+`M&Vec$-@)!FI2lgm5j=b%_zYbYls z#+@InDl2Tus~x#u!c0|pX=C;MD>iNGT-e*SMUvFQtr-?)7}%`2B)t<90G_UXF6*2UngIG<3-|y4 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bg.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..0f64f7db4033db1c49b5379dd3cf5495560e9725 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKX&~1$#W-; zoIL#Q^V{>6&pmwlVD-9HUw?eDX;L_J{J`N82OmDWub9uEn9ZPE$gG;rs+z~DQOv1Z z$!kz4U{)nz(=O}Sq3AbVKX`_zXNy7bL<|2OvxH?%(X$+4W;i9xa|>*-&R*xAvnC*W zbwK7azk=1_<(uMax1=`h$Z~8^u+P!-Yd1+*=9aS9HF>5-@%qq;P0?x7{IA@&^zGM| z&)+{jd-deWi$|^PE&9bgh9&%_6~eYPvO$v!!zWl4tqdz&71_0?KtG+)wMN3fK|QQn z+qYaNW42vfmsRO9-`rUqZ5vVyG8irLxh(T|LmLe9=Xh2u4?MulaDbQjs37|h8KLuX zB6kebHt4Bcve4gcs{6{>>X^0hHFvwu!Co&yeLhD8zK9ICVrhDYiQzO0!x?tw3+$|C zI9N~fa$XVSy&)`cNm}B*qU;l8#mma_%VecDC@H=**59b6yhlspley_$9j)UAdMAwy z9$HykGBf$<=JdzY?Wv2?OHa3dLH_@O0=@gab@z=jNO{a^s?P z;j1g2wYGD0&nhgGynHKqx4O?vtDXC|ub#S*S0m-*!E>1hj~zODIOovmtgJLXkxPbU zMO(IS-L~oCw#()wMFtF9nwcw4cW{2>?(*_@xpIZ(3#L{R(Fb=F y8(Qh97@%QqY^tlheZ{VQEA@6RTC;;;b~(ekD#OxQ!7D)iV(@hJb6Mw<&;$VS5?F-* literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bh.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bh.png new file mode 100644 index 0000000000000000000000000000000000000000..236693dc1b92deb2e29a708a50747a1b9dd8b7d0 GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzhG*1*V5|9 z;Uk?LolBN1{q_6L$IqXiK7an`$&*uO&Ky5^^754{7cO4hv2*9F*|U!`FdS!OIKjwx zoRRSaGxG@+mSZd|r&(D~v9g|EWj)5qdWN0-1SjV?F0OMtJQsL*FAE5qm`R3&G)ye6qmDOcy>mP1z-`(8q*xTK;8#GvJ0G8qettg!1HT6behmot5)kk$Fz~Xk?-_spEpt_io&{ap}^fvuDqqIB{a#x^)vK zOsK7`t*NO2Mh!56Mgi?YAdpWf8fZFWlDE4HLkFv@2avPX)5S5Q;#P9P0VXRe)>WT^ zXI@>(qAIdgr&h_)>FuAVtS^6UYFfl4b!?vCM+e1gX6H7|QBf64veP9=w@m2SG|xF8XN!D5$I@AgEzDhGf&y=!GS^bOz^V}vd^~hgqTaFvoF*EIQ%-Vf zgxo$5Juy>ZJ(G!s;hEDH*)&4#ABmkYcMY4_iThX2iD+E8aVU7ov$f2IC$1gh(71Bv pR@5xfglB?s@^5nTa~E}=wPx58to=%@Ro@L1R-UeYF6*2Ung9Y_KuiDt literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bi.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bi.png new file mode 100644 index 0000000000000000000000000000000000000000..7fa236af37a1686488bcdc52cc6485d928cf0daf GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzjWZ>_c!m} zJ$icP^qI>yZrr$i`{bE3kDol*w0ZNTt5;56ICt>Okq)`K0m$t=9WQ) zf>*c2`h%-%8q`yk1YCG_a@N8b*7;g~-BxKU0y0(xrY{dnTO3fcC8Ka|(u4Q6ZHm>L ziVU(>2j(sdtC*KLX-9L-x2sC(rjRSRPrtByRGNx+#Yn z>K5hiys=u|m&+nU(XB?uyU8G+!z8H7EPAS4{7k3dR_o-sZprgJGMD;fFZYX|% z#fSgPN_=0O^{^o8cT?4w%;ZNEMQ6*4e$JYDaYE1Kg9rY-e|LtR;fx5+HBtU|=K2rq zES`HfKXG@u;A;OZ$nTV!!_V-5V_`vO%8U1Rw0+#M?eFQ6_qJ@hv31KCHr8tbyf=ge zZion6l90G+VfxF>>3@*_|E#RJ#YJb({TZU%!6(^y$r;H?Lp6 ze(~bP`Sa(`o;`c!%$eiIkMG{SyS~027_Gp_9R>J9p!9@i22cfKlDE4HLkFv@2at2n z)5S5Q;#P9P0j4d_zg_#bWNPUa5!EfBCO#odTrYo|Nj#_RfA*}O*2$EaDbLtMjHX`_ zm~%@;S~T{8iSG%Qa|{|O4;L+XX5Fl;GpBafv{h>+>js>-cRExh;hK(|**VR7M(4_| zIVk#<*C!tRw`0PO84AgT?9$o`CbTb}VOL+^l8~V?zh&W;`KkdPao49zH?2SPq|<)t`~bIedc3$aM0+I Sh!|%l$nT!6elF{r5}E)3J%dgF literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bj.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bj.png new file mode 100644 index 0000000000000000000000000000000000000000..290016a5149d8bac81237a7440ae4be67b898fdc GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFx2m*eh-YAk zVqmajV6b3dOk`w?Vq}hCW{zfNjbmkvWo3gJ{7046da^@0B6%tAi zlE{(}^cIw@kd-fxm(N#_43ShXRaY-iSBz2AO4G7vwyS_Y<0A*u=beX5ildb zt<^1HdO-Nx@bH=8af{+2XGEqfPsv!7k-9WBYfVMVmAeS>O!$4-Z|WvQ+W%)TwW z(f#rT3oV>08BU*NID3Kp+D(y1&(z+3wz_lQ?&n{xcOQLz{0Tg9oZVb-1#aAsxO73{&TZKT4-}t1(|-9<|Lt4T&!6qS zes%is!|m5E-&e0ZfBo|R{X5|M_rR}T1Hytt65|x}vh{u3x$d7=d~?_I+(DD?FWf#p z_B*$S^~zy^hnJmxy$-k{$$M8*_NBG{F)M=$_GVxGoSp`_{fhGc5*2V)LgJa4+7fB$ z=i1t96cyJiD{oU%+o7TH!PIoOmexKUox^&12Xu8mT3H=8FgR&sc+t%CoQcV4W8=?u zcE6mQu3B0BbaT6HZ}-s2>8F?1GdH(izP_(KJ^%Ro{|X3r=i~Fy&+kWI;MahFZ-Ifp zs02pnC?GxrPG;3N164C7dAqwXbg;^L067OdT^vIyZY3uiV2Zf8WG2rR9p=3;wtt%s zD`D0 z+PQldZ=TE?;Bhn{II!osma(>~sja%QHLpg>s$8X`=ikhoJv}}oC@Lm`LF7{CRKX_+ z{(7t5%&{mAD$fS$I_uHW-KxKE&(8W^SMK#KlUgvLNrInydq7g``6-#TZ+o|fIw*S9 qiTN>hE?IZdSZ&MJE8*b@pZOT}`si&H3g+|!1%s!npUXO@geCyf4=`*1 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bm.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bm.png new file mode 100644 index 0000000000000000000000000000000000000000..96fb4dcd4f42df3dd8f0a306b7b527d68709a947 GIT binary patch literal 1036 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKfu6vkd5;+ z2j>+b;p1A`$My6-MMQmwPk51-{4O=)dv?y@@Q8OMrLTMYDrMxi+c}=~^xb4&-fU#H z*4_92wHq%E9eKEHZL+!j!i=JM*=3nNA)7m~rYFlDjt^=n7(+}wBVS1YSi1_tk}tsh!gTs1SjWMcBe&hD3!(*?(|NZ@^80h^A2>9mb_bWK?XF$MHZ|{2^9v=e&en&?=j12#q zlJYw*?^Aa6?ufAG`MH}S!d^Bv0%HmoU!#DEA+Yz$L_eT$j7i?^E({&4vK~OrE>9Q7 zkcwN$2?v-|QcWsFRi=dOnPbzWblLlLqZ3o4Dj%B-W%)d9W}vp)9HxgD|Rwy961{) zDtqFHjLgK8^sL07Im{xjR(Nt06n^9F*57i?_`>$HOk7)+we7I3Y(JJZ-CXL`tn_6J zt)C9rF4W6fu_4jln}y5ur^U{nt}3Ee%|f3&PfbP0 Hl+XkKn;S5i literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bn.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bn.png new file mode 100644 index 0000000000000000000000000000000000000000..53460dc3677a5e6a0b0845dd30dbd033106f01ce GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKNB7MZsMea zV$yd`olZ+kn$$nx&xfDZR@U`(^;fT4{qyVZ^{Y3&e);zL^&5Ewg|p|*9XP@I=(Ya) zA5PjjykCC%fA|q_;4t&i6RgKiN?f@n`|z>-nRB*JpS#5;D4#y-pPA)!=n%t!{S5o} zGaNj~ylWTp;lr#453(LSz4ZG-S@KnTKoRmQ3y2<1A_?z<9;WYGr(Q1SjJmW3>wj zp*vzjrUm;Z^K%u-NwhoJz1h8Eetzz(yqq`&h9m6k>%~QMIXDzKIVWmsKlJjNY+-&o zCel($YFS|5h0(J@R@d6&XzVvLdIC{hKvp7R5kIB@~txH;6j~scbT!;3bfs^4s^M?qfpboFyt=akR{0B3Q0-~a#s literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bo.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bo.png new file mode 100644 index 0000000000000000000000000000000000000000..5025e7fb8de78e3fab3d412491f170977216e977 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFXNX~7h-6@n zV`hzIWsPCwOyuNE=M_j7kjRvfEtQoolvgZMl#Y^CN>NsfSJZFQ*UHt>$j~tDFx4y6 zGblB%XtFS=G;x~hp>buwJ`n4NJO|{F};GMbDCx3ll&hmh`1y%_~Zi%@rExqog6?!L6Gaf(1 zc=9yw<*Vv@_v>7}VfEsb$M;`>KYpd0IL>zVEa#<55?8J&oH}K2@|5AjhZdJEnOwbM z_WZfSpFeI-pE?~sX8r1w+yDRmKfe3EdhPk|U%pQD|-@QJ)@%i{V;L3ToBYPN@FI2pHRsZoVvj;cr{(cMm|083; zB(4c9LW?HwKD*?+ZMpN5QnnTSx--(vY8}|lsxh8XX1t=#^*b~0oCa^V4A(tliPx^$ zH|*6n*=qd=xBH*!`!2!rO}xj;+{nL`DQ6fM>KPc$F*9FaWj({ndX|&(8ZYk+0f9>r z5>o^PAIi!;QB=GlFF#jO@}0i^CsWgh78bvpoOT--{c&@9?&x^T+WNo0|G$8Mp8)~i z!oz>X#eGjr{hyTuiq=s;Ca1erHGlUwY9>+QbkNc^GW(| zwx2t!nOooF=;)p~yUEA5=ughywXtg&oY-cqI_0I6^{Q>xuPUosQBn?yg?eUbVSaIf znwFKBoPmjq3k3MBg}K?cFIci>PS2tl0`~G|?->|4Y>mWz{9UgG^1P?3pUXO@geCx~ C%|9pr literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/br.png b/lam/templates/3rdParty/pla/htdocs/images/countries/br.png new file mode 100644 index 0000000000000000000000000000000000000000..734ace94ba415e56b7bfa16a257b38ef6ed732cf GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFkEmG2XpqgQ zpT%uas20+%7T95xInN}bDY_mdN&PES5QnK@Cd&6|QM5ag`JKopS}8 za)jKAg+0o&Lo0PcD~%&69P_3G)-9F|u2l^x)roEPtDKe6Jlm?$njwmT0q6`%1_lcT z<``zyXjaa6PTmAwE@v)*6ak4W2|;f`#R^6F0(r?0N$Ch_r6gs=cty1|HT_zB#Rx@> zObycxQ>|<*?Q9)`5(B+_J>6_wtz<2WW($*26Q@Z|MtMeV)7?BLdHT=s511PeFe@Nn zdO+ax!0_4ODT`CGR%WFxOf@LiH!C-_Z?cP;9@({`lPjK8xkNT}uCHvSP|zf|-sK(6 zb;dQ*N{iRT#ZPo|?T|GtV=YJ_u+&m8{!^Lp^m>D5cOckev^{q_I*H{jb> zzqjvvzkLh%_%Yzyx9}g|2B}Gm zt2<`wF)4p_=jG(=MkhN@Uty`AM^i;pc4(z-Ssu;YYV-H-=Uv8?H!Xe$Jr{AkIg_bX zro=Sl(QD~N&8uez%jD|%G&r%%I`t}Q;m%rPZe^yNH9fr>D%lxoUmKOJ-YsPY^0KF^ KpUXO@geCwmOgTyb literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bs.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bs.png new file mode 100644 index 0000000000000000000000000000000000000000..36c4cbe069fe12f67740fe4240e297448e8f26bc GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFx3;p*NY7wk z=4WQ*Rdn3oSMn_-qm04e472e$PV=+8HrE6kZg47Qu?m>TdOuY3dm`_3ML*`9hSwg` zgim^b$4w$GIpzIwEBND>`P?b}sei@)fQo+s6+eyak{L{nGZ-IZ(3s628q92Qj!8P1 z)%pUf#c5WH6TA)=1)MKSxSx~tyR8`fK+<}ywA~UV_x1YmuS`?lYWeRr2tQ_#eAg`Q zqEo>)x6&V0@yFfrpZQn+4ygSVQ1vyu{!?7XFVB?CS^d9L+a60euaR_KtLVL1FJhl# z=9%ECTZwItV(PA>c0AAwT(9oG#5QGLWX%cplp5A1S_lqyO-c-mTlZ@86rAIAO4Jm)?a7CYLUm9XV?9{kzlKcaFb) zxm~?j}@0$F!{Z3QY~O!9VjVd!9$^#F1Xc)B=-RNP8VIKUJ!<*SbD zTc53_B3HJS^y=g|BpE-M`(|m&4$GYtEG?=jPa2dqt9>uB`0>+X_l{jVt1GIi8C#e6 zrM!FZ(c$TN_FR8&zF!YBmtpBw-&tnrnwGlD7cAPgZk2?_m7ABPOlL3DRo33VX4%4B zO9V8o*e0Gj^XpHaVt+Tkh z)?&v*qeq(^56`wfJjeRQPLJKw?A{#+c)LH~!V0%fN5elJ4gY#F?(3Z*x_+zvGb8dK|5#oT|TvK+xi8&x34=jP5b$|Svyuw`Ly2WU@Y(4 zwyMLc=dYMM<9k!Ui9OqQsTI3l|O^H~@@XU=)u6Tp^GdZzc(pV@&dP zcVXyYmGuB}4tlychE&{2PB_5ygLR3i&esr?mex5>Q$(5%H99d(TJ@|gD@ds?Y~SZv zCa-jc)+1AUd`wbZS!4J9neqJWT0I7?ldosJ)VOi8`{u9D=VnGmPGT1c+I}It@WZo5 z+xSv@-}dLt;1V%1EhxVDXpYV7&emk7+`v4sfD<6m3jB_=~K}`lN+4;4ssTr{b;&(?oZ3ywV5Y7 cZ|q@auz70un|6c z`x!cWSMJd_r5Ar^?|+)J?{VDHV+Ds_)Li{nd*yHK)xXsjepFxlQF-o5{e>^xSH4ZS z{kM4e{*vYUiR~5c35-UcJcYZT`$7+z4l*!!+rYBuq}6zPk!}Zc*JAUrm)rL zlXu-Oy7I5|>i@js?+Qd=jD}l4GNjVxsah)5{9-fH4J(uTen75V)}7Z3oae#w2fd7lsa2 zSq~s*o2QFoNX4z>gab@BZXDU-(<9QlBf|2BOUl)+4o*$0j#Wx6x}!74=1`E96`RAt zjwN$qw%#asCQx=yV&;}LB?~45GXyW#+&gn+>)O5D%hzvb4_L8w$BI2W_ifv^a_gF1 zyH{@((719vI`Gnkxa&89qCzhR-wb3B;rdj##ccbQO~nPDKYcDgU(U*vdfdI0t=-j4 zeMwr{5*77hjIAQ94SJ8*cX?b72=|BFz8fkR?RE#+6D?8 MPgg&ebxsLQ0Jmviwg3PC literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/by.png b/lam/templates/3rdParty/pla/htdocs/images/countries/by.png new file mode 100644 index 0000000000000000000000000000000000000000..8f5b8499089ec901f1704cfe84af05a0e34b181e GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzoKV&)zs{v zjqP(s=S~aDUp>7aX3b$Z!@zuone{9y??qmLs{*ojWECGON-vgHT&t+GURnQ@zQ!&M z)Ay!YyS4O=>KPm}FuQ1Ka>2yuo0HowH>XEVZqMBOfBOgg4)FWz7w|QJ^$-KcQD&aw z?2;EnwC<^CZBjGbt84e#%KW&o({($)uU-MKd_unl^6g_4ILM{EU3iHq<6>3D)w*2E z4S2U%OC0glUhJl`*4OY-yxq<)tJ^ugXVN@xha}v0Gp1pqbVg28QE6#K>@r zf#C!r<8emDbIi;qn3<0;GuJXPon~b{#mahumGw9)>lt?Tvz(kKI62R8ab4r(y}-+R zl9%_gfWRpsp=%-{wLCmGBqYvBNSu|Bcqj|>w``4ozyn3a`-+NJ6coemOaPb#nUQ=Jwsq z?Y6z$BS*)FPEI?_%zk=#y>fFqY;FC^*Z04_|0_?=7aktJ{QZ9g1ibU{`56%K(a-Nk zVBq(_z%Ky-Zvz63xVnA{5C0e)^))W;X;jpYl$6h@slPHae`jT_^Ywj}mGP~#^lNF! z@9OF$p`jP@^MTO{jNDOxKLnO6&6@;N!IFlW1l)hadGurByMGfkS*PaCHC1(37-<|C+#I{-kWkU7 zSyr!RayL4?F_t;w)8l(qcTMiww>fKc8l7bN+_X-u%6b*G>sOW4tyxkIiel-`etB_q zfs(AQk(z~qjtc}PPi7S7pTA+to=J0-w9J^mVD_GYVM3A-SJA_T&p>|nboFyt=akR{ E0D1v1E&u=k literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/bz.png b/lam/templates/3rdParty/pla/htdocs/images/countries/bz.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1bd5aed004286949a0fea90b081202fcf23607 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFZ&XWc(nxO6 zP3tkrndn$rY!ch?~efDVMa+r+Z7rf;oX*8)D|{$XHUXnBHboyk_;9)$@O+gq?0Ey)&ct ztC_|b8@)632IutEx;<>p``TZP4}2Zzd&Avsd2;B(0JoiqVRs_@jwFU%iVHqdl=dSz z;%a&Jzx3E!)p`Gl(%O_;~C|OL!g~eyKG7TW;o+>hdR@t>2d{_$Mz@Z=!#} zNbii9(HUEd8(wY?4fLkOM!pLUIGz~$FhBcORK&U9z}*=sPbecHY3+03bDx@H}lm_IA+=+|AlpRAp=tNY}cjC5( z^mK6yskoJ#aDb_zg5`*dN{Yx6RoBX&tu03zoiZ(5HB&!&mVRx$cylMK>QV>AvjzVd zet(*_?%Xjh*1$)}uD2+@c4eNHwfkm+1py7a*R>VBZn;^kR<02lIirQCwT5Twm8(v-eyMGB zx4i8lA<5ACDcQ*Q(&kChS;4~H!k-=`H9F|thDm1)Ft SK1gmK$k(2(elF{r5}E)I+kQs? literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ca.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ca.png new file mode 100644 index 0000000000000000000000000000000000000000..d06843f5f8e6dcb8f23724980d792bebede9219f GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzk2n`x6hxR zJh*>q_l`?DHeWt`;G8V$DHWazD!kV<1uyAIJT#HL>!N?eUU!p~)>#+B|0({T5`2EA z23`qwyOthyA}QodQQEnZjK33`&Q;_*>2H28qwiQ(t&KCW5( zbIY2)JJ$a{e&ElkBVW#*_;vZ**^TQ?2{F`jF`N@&zNaQ}MPGiohV*MI?O*;*$33k7 z#RohO^M0Ngf2F7OEFa?;0oL;}LN^T6KiQccH`hCFWBkj<>6WwAZY!hbUJkGP-TsI9 z{*4O!7Vh^gA^c@xIe%-iw_3xiQ zZ(hHC{_NTB-@kwT`t|eY&u`zpefsq2-Me?MU%!6!>ebVyPai&fc<gab^EuDpIK zQLVOd<(V0FuFDSE@oFqt7ry4o)r0S{R+yDeieEUvIKU%(f!3Z{uO7FsXHWh$7zw&B zn6NSD$(_av*#?5 z3d>676!}zeYU;d&ITI%2Xy2C%QpsQxDSCK%;ta#H=67RPtqtGSaGysb<>=(M#(SqO zUAC*WGAUr+PQd_;z}i2Y z@1MVZe(Bn!U3+(NWw09+8y2lD3|kb)9m~KM$E=sB7COTxa#nzBhLB~QzFCp3O_i}n zhux>IANQXZc<|b9(=LW>`&hT^X5D>AV%}oKmFqRO?9$tR#N^B+i;LHt&YWU6dz$&o zDbA}`cpp5Jy>eMTB7%4MQt3?_mG|w{I(5R}$`#X>uiVnobUZyxl9HkyJh*>7NAY#7 z{b7^ z%X?Ws;GTrUB?*a#va&DK)Lv_Azcn@eYGw7+$?1oi+b@6rgZ_RuVq;H*hu=;~x_jr& zzkmP!{{8#)>(@`8K7IW7@x_Z5SFT(+fByXP<;#Il42IR`ym978H@B_|wUs@U8;Wr^yI-O&-1KUiB_jx;#c990cEwJPT=uZ~vJp+%1- zIVk!`M!K?=K6|F7#%r3VXIZ)6!l9os#pyaTuH7&zz4vD3=4VrwT5Xi)*v>IozNO6X z`lYhs@($vcy1sjT8=M>!5B{r* z3*9T6Rm*(%uoH8G)5*e(7Y!$-{++C~IB1fP@S{ej$d@ZKC1*YrRu2~ud-DA0vxLul Y4B93}ALU|qf-*aUr>mdKI;Vst0KUjx?EnA( literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/cg.png b/lam/templates/3rdParty/pla/htdocs/images/countries/cg.png new file mode 100644 index 0000000000000000000000000000000000000000..f2db5185056bfb0948fb6585b194b4a5e02e9a64 GIT binary patch literal 1040 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzhG*{P|Ltj z!@zoimGd|!?@3;+4lbcnLK5dBB+g0*P7sv8pdf!b?0@2sp}S+2(!+>SH4A7l1B&gy-f)9)m2&?%wtvl1~Eppr?0i2KG%NvPXFa={kQK;KmV}%`pfCZAGhPj8IB!eIB|mU_;JP) zCzy{NV?KSF_0%cWTp-_1~;EeZS7?>lUZ) z+ueTd^!l|Y;PVE%U%PyN?elhaQhuV4PYPu<*Jd3yfx_x}~>|0^KiS5Uw^ zAD@qYem?>Oe*^`74-Whi5bz~1;9FoIFs6X}nB?v5!qCAg>jC8K z^K@|xskoJ#aDZt>_AQmiGnMbS(9(3a{1;@bC(bn-YE9QcvTM#oD!t*U4xs zS*3Aq^>dNe`Dv4HUAlZCgiR!9n%IT%-jBRnPvyMGUZtnY#C4KOa))~*d-Rek<=d`@ zO?krH>cX>kkuCGt$=OkjY@*^%m|A;k7Tx*dGkMjlYANm0T%U_JRI)QnF40}TIx*)Y PD3m;1{an^LB{Ts58K*xV literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ch.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ch.png new file mode 100644 index 0000000000000000000000000000000000000000..9899d788cf85c09a246452fb558f94b4dfbca4db GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LD#y>j*VvEv619on>c z^OdVt9z3{z_wJpuXV08IcJ%SRyT9JQ{qXX|vC6{JhYxKOW#1~zyGe?7g(S~*If1Qm z0-NOo_b3W)l^0qmC%8{p^st)59(BnbYLff3WY=oS?$A@*rLTP4Oyh)^<^dD+bJn^C z%(PlH6)!oNo_8|cVPkmH(|U)!$u%##osMQpY>dwPIP7$>TH|Q`ILz~$uhT(KyW>8N zyF6@9`#B%*vcDMUw%N_*Zlv$o5RaRYzI%P07duRngko6d%}y{)g8%soAS z_R-nXkIkL=^~iza3+DX1aOVBvM_UEipGF1#`2PL>|NsB~{rmg(@1H+^e*gac>({TJ zKYxDx`t{SNPai*ieE06%n>TM>zkdDV#fzs;pFVu}@ZP<9H*VaxcJ11gD_1UEx^(g4 z#S0fMoI7{!EHDnvoH=#s)R7}c_UzfSbLY-Yn>KCOuwnV~F(~XudfG2XCtD??m7_2Kl1>y%fm z$P5l(6Pa~IT>9-Ykt;VJYOhRt5gf!SGV8(|fea<_Q}?76=cURA9pn;8(&;epdi*pa z`^l4YU%sr*X5@N0-D*`z={EDL;ZxJ9s|`ySTf0`7F4^i+DU}s8S;#i$=FA4CsX3bt z)$C;4`t)Swx@*(!?sZuBORTWYu=9k>IuUjDW~!{(j)^ggpJ c5MX0qFx(!X${YN2Hz+_nUHx3vIVCg!0Q{wHRsaA1 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ci.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ci.png new file mode 100644 index 0000000000000000000000000000000000000000..d536e9baa2bbf15348baefe4c6fa25022fd29975 GIT binary patch literal 1040 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFfA;p-`>*fs zJi4RNt7tY;->lWZs@ueMsh!IVyMT3G{!2W=HwJ{Q4Nz;7Rmzk$nxLsssHjzH%d|Q^oiOO7`DbQYGYvJ`hcj7K_QELlD0?1tqV`t8e`RI zcJ z*ID5|7sS1o7Wr>w*3V@br$ZRdhB2QGV?G({TJKY#xC@#E{)ub)1BdiU<#>({Seym;~C$&-f-5O}M{R0A}NG0EHAg`tC0 z)&t1d=jq}YQgJIe;Q&)iOwi^o?WkS9e(U~>JUV5oqm$jt){E!n*8bfq)|A5)#pbYZ zNyCXfF~{%y+viuD>Bn2^yC9${!qIO+@c!6x>29v=>-vNOG_193l-DlVzkKh^lM}-I zL-{m9LfGmD(!|Jd!X^ExMY%~rFgfhLn#xLSXyA6K>5VtO?+ zYG%PnuMUP*roDHQY`kW_UagheW3j}gwZUoMyLB7mp7LJpG2NE+=FuUh8+(`;Y9AS# T$=WP?9~4BMu6{1-oD!M<0m5V} literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ck.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ck.png new file mode 100644 index 0000000000000000000000000000000000000000..271f568bc7772a041538c31346458c4556b9b82d GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFpJZvZwyq(` z(PdUv!R+Qq=eD1iTH7Dw;J<42%GTVr!hrI z#NTcYn z%+=*ZJIV^Tl^1O-$={HgIHRUwcU9Sz*2euQi4%G{4mH+osi|C1QMxEAy>I%IV;!xV zD$C~0nR&9YZdqb{&D>ch=FB`|VcOvC(i0IfFFSK-R{H$fs@+WuI~(h^cC>9T%%4(O zK6lBY)4JMqHr8!fnUkXv7o2Uau8#^myJ*hon&M^kRrfcnS{D&`wXS@5YW&&yipy;c z6N3GB=A_SXuuId@0>&&ben$bG5QvKX`v)k^nB?v5!qCAg>jC5(^mK6yskoJ#aDd6; zXI47LN3L(yCr_+YJ@#6Pxivz~Z>DLfkn!r4;1#8wv4wmA8Ta0BURL*?eOl#O#4V9^ z*YEO)2t7@jfAYNAg$I{}gr+0~C9sM-5|}to`OfLHi?7H`osc>$icMrwhN(dKmbVk9 zyz($Iy59eWiA!`@cGZbnyQ_b1eVU$rEbBXCYfr|uDMXhz4 z&dsxnvTFM}wQ`ynql02;Wh6Um=>Hu&m$%GXb&i8K=N==2&IH5WvUFz~kjFh;{an^L HB{Ts5ayCn| literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/cl.png b/lam/templates/3rdParty/pla/htdocs/images/countries/cl.png new file mode 100644 index 0000000000000000000000000000000000000000..97db9ec04d443290ff3354a602bc22fa97000df9 GIT binary patch literal 253 zcmeAS@N?(olHy`uVBq!ia0vp^q98U0GmtFwxe5|U2=EDUWnj3)!0_MF;+~n=|7wOF zFZcgZk^j#zFuVkcGB7-^tZrfGTq@DiQu%-HzGGXr{oi~1|Nq|Zo40OWyZ!$?5PS!M z_y55FXe1_x*q$o^6lF~Ec6X6y;g^i;#IIKSRD?duixpnbRB#40;UU)d6^+{X1k`m5+) Y=EOa`AxVwb-hyoQboFyt=akR{05V-;mH+?% literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/cm.png b/lam/templates/3rdParty/pla/htdocs/images/countries/cm.png new file mode 100644 index 0000000000000000000000000000000000000000..febecadf56703f724528fbcdd5bd88a636354857 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFx5~9<@L*tY zXJF7}V9;S;_GV`GVrKPX<@Dj?4dmrA=MoAQl8BTLv=fw1P>_$8mvom@Pf=G4P}GXl zvM8~zD7G+4GP2IL_Gt77=nQZxcMI+~DL% z!*l0MPMh)cpQ;S(Iw+LKt;=S!7 zdt8a(j2QbVUWU^GtQRDBZzxMV(pP(9XZpp*>8Zcl&v4(5;eJ1&15Yq89A{wI&BSnm zk?}Yq<6aiV)9lQrn3+#7GoNH--NVXyfRp_MC+9^ju5(;mXLxx}2n*a45xFKJazIq* zri8?K35kpHvX2xM?<*=^l9zv^ruI}@`;?~oTYdeP`ucB7P2XEtJ+QF&Y-jh)$?3bB z+fOgAo6e5E{r!Ih1bht$coh*2j9g$8j{;mFFm=yuYoHuslDE4HLkFuYFc&Z!^mK6y zskoJ#aDXY|X3GqdXa28OX`k&gD*D39$hGmKx5mr!@3M7PpY1gJ#@ENf^|9lW_6y&f zm1{E2^%`<_a4>Oop3v}?F1wPFvhBHQWJ~)m##WWkl%qVaHU|Y&e}25?&>!0dr&mQ; zOjb{?X1<)vEwqMt@0|vxuAoDAqC$P=3NI5^jrz62nz?mJXr-lz^sQgZ&uMv@eJ|O{ z&ei$3&?tNRH=mi7+b!;0IeJh`L*>NLL+ftaW*(bU*}759ugvVm9%hEua||zDf5N>3 O zbge0cg1e_1z1$J`ttq@-h@oD9;j9wp87by&ajx?!LjTJ%zL%w*5n(+i&wE8z{tOqx zTm{L$xhZEj7|(DrE>~6j7!q*B+wFgT)>%e|GYkx8m>B9A7|t>>o@HV@$IN_&nfU@M z>ls$oi|p)YIXTbra$e!$y2i_Uj*s_-fWUbnq1z%N_ar1PNk}}Dm3^Y9ctt_}ii*Mu zHMQl^(yz6(Z>XzpP*i-UufIuId8eA%U48w#2Ksw6G(MS{?$^>fqN{sQN9VYn-d8KD zQw9cSjSL@JSUj|}xN2s4$;9NmvGET(yI)RDx2&w5J32hDxBKJf_T16&sgu(`FRxc_ zZpW;x|NHvB^Ynb<;ql+!|6f4BCm)}`fq~!r{C)=p{tO8C7!dF+Jp5N&+~1UxAE~MT zv$BBU1`It4L8o!jMW7LkN#5=*3>~bp9zf1KPZ!6Kid)GE2be@GT$NKS_V8SN5>nbL z+nn6!6tgCWX{F+!Sw*LAxn28b=;@#sCswvG!&|Y?RhOB2|A`|S3nnZv**`^AzJJZG zeKVsYYGMQeIxLlsUJeLgNeXaU!5^SuYkFZyv|Ltz#boOM4c%Ko?oOOp0S)ck0U9^s z{9XCdCamAA8*oDSozeowb$nqCio(1zx)1HWJ2&Ud#!7aE55JV}ocDX*3-YKn{8vpD7Mx!l%npQqPG7uRbMQHO(qZzU#Qic4TrZe>(%;nMEr z)$Nq9oUZONQ`>u)wo8wZ_f)&styUomeY5s^ByaLe*&LX=Kc)5LSql8(j zlvR(8_iSyadegue_HnC?ygDobC%VKf@Xy^In6n|QXlH2t#^n4ux3G+|)~O|p6B_&H zHT2HunzqEsJI2O0&LJQ%B&9U9v@JTLG{3gHfA;dAq{6dn8P2R_zPO3~{3fB>dqrNH zQoDOd|LYB_2gmLHJ@)!^)93Hwz>hZquIzU^vznnko8jzQ#xrXe&#h&?u%7kI8rHLG zIj?Nyy0(q?+?v!9<#W#)8xl3yI*&mb}u#hbKmXx8ONumosMs?KDNR7|0CabS3KWb^7#MQ z|KFp4Z@2w^KM4GJFW}pq@Lvz(US5j)`y}Q6^NjBgQvW~CI>OIzOo;iYIOjPT-kaL8 zXLaQlsYFp zZ?KM@@0GLFK5D9S?9}kWm9F6pu|@M!t5!74ILWDBCt}qlW8bOjHABWU(;{YtP2^PTJGL>7OYhH|dUxj3Hc^3X@{;>h<&J8r95>b5X{vw8$!4de>0TT2Ywix~>@3fE zyB_d#xftSiKG=7Qr_0sI;44ufH)F%E$ArB|iF=$9H_OH0XNcLURRbp zEiZagUv(@e<3oS<$yqb5uUc_t@xmv&ckWY?zp!Z0zkmP!{Q2|k+qX}jK0ST<^vRPa z4#LN9V)J6%)rsHf+BP0f|&<~viTe(COhIAzMaIdeYD zp8amwvd=45UfQy6FYZaI)42A)vIqFJot9>=#2*t zzFxcbikb6FZ{O-8N8iktx&7j$@B8*YNX!u_wL>J_wVQ5zdwHe{`U3jhj;H@K6r5Z_U&soZd^Wc>C`J_S!i8Omy5ACpYh& z-9N^I3mq2zTX}cx=h@z;yt-FwYoB^8)!;Na&G76)t*(B54<9c-Pj7!#u8n7AThEPk z2#brneBjp6s|T4hj+`>9PLkh0rD@f+m2)TeZd45DsC@Q>^G;=5qvFFyf^H}vZt$`%Q~loCIB`GhL8XN literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/cv.png b/lam/templates/3rdParty/pla/htdocs/images/countries/cv.png new file mode 100644 index 0000000000000000000000000000000000000000..6204e60b5a09994abd317624451ab7c747b2602a GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKe}Vr;hnpm z-n{eu^}8EqFI+i!=GFa2kFMYP`s(eb6|47d-gaW&!A~z#AyW?zc*9BfK^PFru0=<{`dCW04&I|IH zA0D{E$+j*gbbW~5f~b%+_SWTIZXIq;4N0+^(-XG``%TVH-JO@QFFI^)pl?s0Z&y<6 zvXZ<*nJF78ijHQdZHNk+QeSztru<}e+3~`hZDob~lH=z!R-dk~JYH3Lu&MS;bM2YR zl6{RerFk_-{oLFOM<=y379QTw^X&He zqkHD<*)aFi-qp9xAAJ&SdMU#AxP$z4Z@rVzuE)(~j+x4wwNu#}Z2vhw>}rbNubPxs zC2=o`W53m<|LCaz#wjpnM*+bQaPU$#1u9}p@^*J&=wOxg0CM(wx;TbZ+)7S3z%*s* zTIS$nn;z3CS9sUR{5|C0q;^k}^WQWpudFJgZ}S!gIw)#hKk|gZIMy{ZSK0def)fP` zCghd`?U}P=;k2%Ot&{UzI>ZB3?AWs}a-Y>&C1vgPE7omU#-MTJ{Pjzk8V9f3J*9L+ z>x||h9+6qbtJl|mTB0djwfyW1+u~nGRyrs$e*30p82E7>pPn9{S)5$nf`G)dD(BR) zpu$j5A zhcqgif@@BP;Sj{Q_j0+w<-#zi7>_vw@g(AL5C8y$B9S1@cq|r+Mx&un zC>RXlI35TD{C>aB=fg0}?RL9dE~nGEv9aNBI93U9cDv1Hvs$edi^XIz>GgUPMYUS3 zTCGM91cqUyQV9_cfPWbja=Bb8l}aQMu~;k;iKeHgCnhF@LLr~e=ka)beSKUmx4pfc z&1MrR5Lx`Mf9$|zt*OsTIZqmsBg^UDb^N2gL)ZA&-mq&xD4+>Fu@eM@f-K3Tms zU)oV}AvAC)4;rC;r_EY*S;JZ9hoAEJ&F8LXr({tMUA%UGtL;5g@T{$4O5D#G_}KgM z$<4;I%+VsIY6VqU4X~`Xpy=4q+=85g!^q~{+xIrTP9Xd3>koT!$Q7W2bvh8%_BXug z`ElyRF7lm*1I=Ke@_4}*M{#R?uqK703-nZ&j5Vw?SL-ucDhg*hN{VR|PH|)9xaw86 fS+?IeWtvST$y-n6A5jZB|5!<(_6v@=lA4Kg239l}Bu{f#o^|j#xhNBtfY{HN(XL z(Z?7>7BGs=<&>VyD?gu0EK5jrsf6|_3C)G-7ONC>TMZo6Ynk;~c zKUcQz->~KAvE#=dJh*@M?3qu`pPgf7zQD?Qj-CBHFYgrrf!iV?_ar24%gWwUR9q}A z{YXvinYQ+7Ma38T`soN z#N?E*@h3aG3udNYoSd#&S$%VJyKQfG)Y9^SlhY3`uM76}Pu<*p`ucwH^?m8-`P1M3 zXF$M5Kfkv=K0g8jzXSw)3k-Y$6pW1g5*792=FO+KZ~y!9<2rasK?JlPCZE z`}gr;{Qmv>*RNkcfByXOa z@7=q1`}XY{H*Q?JcJ0cQE0-=^x_I&8g$ozXojZ3H7zAg|%$++I7;(Vp8wHevK#lx{ zB%prAByV>Yh7ML)4;rmYZ0N0B0?f&rmHjG z&a}vMDz6UaP1X(Y@R}HYe?s-4C7rgiXG?po`Z_39mdXY)`f|VCcWzo+*C{vc1rt`y zYulvLKd~}$p|59QW}sz&N4WRV2&bO@xt2Pn`xW#x^j!%qSnkNf3-mu*D>u6evS_}KHDyCK!iy1kxF?$wWE}pd5 f>F?gV8!Fiu#9kOAiATIi0R@bwtDnm{r-UW|l|EDw literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/de.png b/lam/templates/3rdParty/pla/htdocs/images/countries/de.png new file mode 100644 index 0000000000000000000000000000000000000000..67e1bd53a66ef6f37921aaabad1102d8d82b0b59 GIT binary patch literal 1038 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzhG*1+{*fS zdWMy?^|1*I$0xB~o+og2j>MWa#W%Z5?`^a=x5VW8akmq_3@7`UPfcMxF@f{qbly8l zWbdz5yfjaKNsaWY&HC%xly^+f*gZk(@GQNHOH98Vbb7ee>DhL--zWTkoeubT#P9el zgRe&dj<+xzYh^yu#eS+s=-O0~M~l_AwW{4%pnsrW_x)z8(=&{3FSq---|O9WpC1PU z-|Y-I!NYi*hw+>U*GX~Sn@SRw4b-2SYVR}FIca71+1>7{lhv;<-&cX2F9JNiMFsxM zPC3oMaGZf*EhEDTX6Cib%;#BI&#T(`s(WLc6J6@S^*v&p}xM6fq?;@o(YkW*(oVmSy`#6sc~^};o;!{0RjI0{%&q= zPEJmaj*b=<7N(}AMn*>Z`ud8Bit_UEva+(0l9Cb<5`uz)0s;cOyu6&8oUE*@%*@Qd zs02pnC?Gxr_AOp;45*qh$=lt9p@UV{1IXFq>EaktaVt6D0GHZEv#n+R^9~v$XVv?= zOM9evl}8@siEd3*FvVTu}zy29&+{CRp!=3+!3FWoA=n%G%E{j z{#IR$$2@j6-b@qLU2gvgwTo8M2u8wr>mdKI;Vst0D!L}H2?qr literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/dk.png b/lam/templates/3rdParty/pla/htdocs/images/countries/dk.png new file mode 100644 index 0000000000000000000000000000000000000000..9404cf0bebc31e10e9102c799d797e52b4626771 GIT binary patch literal 1037 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzbGj5Lf_zj zUjE;_{Nua#oH}~s|IZ)KA3iwC$Z&>%p`L-^EFl!cb4FQ2}F0S)JLgz$9?@379lajn7Au&Zz@S&{iZ8^Coii%eh85nHR(ps*e@!Hbz zp@qd&Gt)~ZCg+Wff7sdma&p>jWc1b9`GLLNA2+vyW@b;Foc?)vowc=j<>q$G+WNn* z?+!k74Ro8)n~;#-v9VYD{9Z;z{!K~wo|<|lF78}>{FR)X+l7T!3JRXo)I4lxc-Pr^rlsXV zd;7J%zB8SjcNZ;svuV?>Lx=vKJb7XN{u?JxKDm1J)8oevFJJ!i?%mTHH!dAJ_V44z zZ!cf||NQyPp+ncsoqKre*46Xpubeyg>&us)pFjWj^y%G;7q6Z?xpemIxl^ako;vmI z+qdV>pFe!~@WzcB7cX8sfByX0vuDqoIRlI-V0?`NDu%%HbB9j>jblvmc6VXuV3qX% za&~*VIEGZ*N=`VyR8cV{=JT!K)RrBrM@35B>~(P3WLCISw)4=PKZ}Z9U7OeFpjh~L z#*I1aSnT2iHAA(xGw?5%;A!HWQRZ(WKG|k-Za_tcd_c$AO^bFcU9Yu&!G;w}_AJ`M zqj4qTeB||O*P~)Dhn&B7A?7rnNK&uKlryh zcI2x}rq(kJdZ%7^G448X{i0KRG_S@Ij*`H%bg2ujde7W;Non2K!_1&RN&B?;)#+8B OAo6teb6Mw<&;$Uc)mrTU literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/dz.png b/lam/templates/3rdParty/pla/htdocs/images/countries/dz.png new file mode 100644 index 0000000000000000000000000000000000000000..26919b18c061aa593554808378053d2ab17d41a1 GIT binary patch literal 1037 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFf9LM;P}Vk9__8owrhfHA_gLSlMfiFGDN?Lkt6h zB?EIDGiw4XYdkAwA}4P;FK-I3K#l;H3zty3kVKAzppT$zsjOnPqI`+Ge6fOjp}b_M zqtfE%BmPMO|MXQBHvxQNCkzRWvHp4I1?g>hvAE9nzMk@+Gp1#BwTUNJvI< zsl*Ehw%I*+aR2JnD`(H1xpd)7sreDQ3h0Q2#w6PAc@J=(tI3qg3YuBz_x^(Hng$w7;pFeZv%&AkSjvqgMjC8K_H=O!skoJ#aDd6hCFE=FnG%+YS6N!8 zxi%X)IDOg`_N>ZFF7RdOQ&-ELoeqkUzTDo<)&FM{OxyCv=GYsh1rypjw{C2XpJP2e zKg-cAZJ~I;i37(%PU~7uus2ejXsf(`i)lQf7Qa}s*`VWY=VN&K?~iTF#%6v0`Cei?=@6kYh!TE z&g8JC{Z)2`BdIBmb26_oGM?e&Jj2U-PD$yKy7~=mokK=OXN--HSy|n(w7BTv{K(Vu zorlL8PmgB-0Y4KGzNMu6&B*wYy=HUTzMXbc8yzY$X3n0K)SNYW))Z?$tEfEx)WX=< zln4(W*L4T$mu#unakws{NG2#ky`x|K%qfO*XIajk=DK#3_sS)O<;xZC-qC;k(&oVf zyXQ|G|NZkSEmi*X$>;Ciz>gmTu3T~Z|1YDn!|}{9hWcuT3ny969Amw4Nuax(>(W_? zDH8-A-jRKBPjT){$#*aGAKulOJW=k`8`Dkev^K5RdU(g;$7j1=U!3;tGTOb}=+Aez z$rH4X9kTxa)A!v=&)3g9{{Qs<_cP%4x4@rY0+uf|pE}9#+sE+bi|u}Wi+lMb^6!t7 z@1Ilu|H|69F67kl^aH!25ARQ2wJiMd`GPywORrume{iRxr$u>EU(kXnx|yj?E9Ny# zE{SNUE}v7Ed!;?@M5xB(_U7l=K}*|OK6tq`<>s7WU^vUne3p~*8ZYlLDai-2vR4%3 zmrF};QdWLvV6b0H>y&}PRWs99Zf;*gf`E|=jN(y%D+DAyt@H)TF(!GtyD)UH%6b4f z2RvOILn>}1Cmdj6VqR>d)g^SeiMh#1>G7o^N2iA?pQ^s|<172tCwTFsh|r^r2|%M+qiJojmr`7=Qr$JwQt=r zA&n(#S1M`erX?*{ovgWX;kq>(BA0?DPo8L$`_|yq<_Q6zK@-@yI6FRYc7Fcl{~~j< zkcjY8aW<~P%^Qt2`@GG2|K`=xhmW2luygs&w<)|dC3Ewy=f-DeTc>|JBY9&FGsBex VqoC3+&7fS*;OXk;vd$@?2>`c^Y`*{i literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ee.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ee.png new file mode 100644 index 0000000000000000000000000000000000000000..e0a902ee770989ef2a3606a2a8d5b8d74bc583d4 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFx3;o2Gd0W3 z&Uy6c@$us)eEfoyR5T2Y%^jTFf;709=MXkERPN$`&{`>du&!0cPe*OCS^XIp3-#&f%^zPle zH*em&diCn*)2C0KJbCo!(Zh!iZ``gCIqXJuu@#l=NMMTLilhlPa&1_lNM z1o->=`}z5~ySux&xj8vGSy)(@nwpxJm>3%y8yFbq>+35iDJd!{%F4=0NJxl_i;IYe z2nY!9^78WV@NjZ+va+%=GcyCD2^d+UfVv?txk&yM&_u>0Z+91l4pvzYAZNd)i(^Q| zt>lCQOf0Nddt}#?PEl!9c@naP$G6c*iHohw%xzWBp+`oiX6;f-b5N|zjjs0QjWi6* zbSrKfK?y1A{|MSa=xCycF0 zLP0@7$%~zsnVpn`FKsGnbc#Ev=~-x)=-6{maNkPFcRz3JVP;tUK`->o#1E%IA?4}n K=d#Wzp$PzQ5j9c( literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/eg.png b/lam/templates/3rdParty/pla/htdocs/images/countries/eg.png new file mode 100644 index 0000000000000000000000000000000000000000..f81b276995aca76d17859f9bef3d41e1c5bf9b78 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKP(}2R8p#4 zQtH^g{a4SOkBp2qGc!MQ=%|~!=htsPGBUG{pE~pA!>8{*f1Nyi=J3&DO3KRLfBpLS z<;%H?7tda}@cZxIUw{5QeEj(C{rmC?@+VH7ICgBSnVG4tuaCRCTU1o!$rA_f-#&8r zXzQt+#cy9fx_o@r-@~0IQXGuyPQ&xVduYXlf@2#onT?>n^PEI?F zjDEPeU9`4-v!+-w# z`TqU;w{PFRe*OCC)2HXppFe&2^vRPaj~_q2bm`K?ixdrx%?Plao`!6yOmMK`79*pdeGx@XU^$sF>*~k zX)!!)ljwZ5uSH}ZM&de!=MD^_J@WWHzO^7K!bapnHqnBd4j-RoDh dt~8&uW?1`F@9xZyrU#&)@pScbS?83{1OO3URu}*P literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/er.png b/lam/templates/3rdParty/pla/htdocs/images/countries/er.png new file mode 100644 index 0000000000000000000000000000000000000000..f96b315c7ec6618f81fd772ba56bbd71e2f22431 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFpJr&XCLrWQ zS@q7Ms_79)<-U1SOQtOEUfEtaAt85J^Q2Rala3{pE{m<+Y9H0(U$Dw8af(OEbf1j5 ze%bQ_auVTJ4GZF#H99d<>t?~G;txHe~JywI6y)7J}+hX>bAPr*F_(xo=?w`%>*=Q?b&;o54)bEhKUNpsw-riAmwVS8MZ4*D8wi*S0~ zma|!dXSoK?v*z5Z&RS2SoR0fjEpRq^pB|7W%D!8bcY_lD7IlH+hEn?sWsjPx9JW^7 zWu|i6M)QoL?nzgp%bup!{H#w0J3mVBdzBILDm&s~dHQxKw!Ly(`63)!75FwQ3G7r6 z+NvVBOH*u*j#Rar&@n^B-A0Nns*+R9wC)Bu-i`LU8R`8xJNiy$)Pww_msNQsf*e~E z1P>^S?oyX}8y|c(J^EEe{yJxy1Ad;sC>{tu`k(Vy zGjp=&qelx?Rnadyn0R5r8_G%@8vlx zeDk2-yvF`X^H%L@TXyUu_ss?;^Qp%BMZ^NqqPu zG+>fABiGgNb6J^h)1N(<7#8?MTwIiiD^x_(cw_NtP4APZJiIzMxf4F~F-WT!ea!j) Q_8iE+p00i_>zopr0NT_=82|tP literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/es.png b/lam/templates/3rdParty/pla/htdocs/images/countries/es.png new file mode 100644 index 0000000000000000000000000000000000000000..c85447dc230b9cf527b07b0c0fbc27e79c871d89 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFuZ~S<&n#cH zLu1Qf?RziO-hZ{ac+>dQdE+1dyiS~AICX~k%sKXJH$)yiReSbAd+ScMGv{;<9MOIM z$?En4yF2&ne*X4)`^o3W-@qTg0^fcNxOmy^=kJuCe=@#&Oa1jH_xKUU<3|}!onk$E zhU>z4-jm09FJBhew@>8eO^F-VByQi4y?0|AeFZGu!RebZ-bl*OmW5*5Fta$xzAg2RkY&EM$?12dbjH=?zK97 zpB``|nCViYK)?x;3HwKT(VJ?3LZoafDX z`*j6xxyhb$lYbGR|0UCDyT8$+6vv+>{$EN0zOPJgergc;_CAIQzAb%IJu@?{mN=8s@i&V>C>s9 zTT2}j#VVGETt&B4XNnzf<9cXHafx%Sn^wyd12p7fcI YK|a;!^;3BYt7Wi*|oXr+aqTdl&x=0oRh~8Ex?%|mN+LfcX^p?iHdH8 zzEy=aLlgsp1p|XQ19J>BYaAK$tduz^9S1w~GPHu{`G(IA zjGG;qxiW>{mCL9}+jWwi|6~vUDW1hEb80r1PMM>4?7aS+XI3XK**$shb^D3W)Axb5 zp9CB@>Gtwd#`k|=r_S;`eeU?;mD{gBem{Q2ojJ{T_6*;pOHx;^DqO#&aqxi7nbSrm zPZ>UZXmRP1>6I&HH*Z)yed>JtnDx6ip08hd{`(j3?VI1vp8>yq27UV${`*(z_wQ-{ z|7V>sV7MEh{~*QgQ?bv-vVb#&%;)Wdc6z8C3D7+sZoJo6>RDO(83u-P%*+>9S;y{K;V*u#6wxxD+=<^wi1em7CjtfB$cOem?^OfRPJ~;!%Jr1fC!Mw+tx9nB?v5!qCAg>jC5(^mK6y zskoJ#aDd6g#OKPKH)p?=UfKG!=ggX%MyFY9)B5c`|`Wv0(%6w%W5@bwCL#9bgbN$2EwPk&~vt6^8Khnj6J{Jd%Vl@N1NGlte> z-%GY^D>vS_!RY*K+u~PGI2{)5J9ll{x_h0QpPid;oP2AF=ieK9m>F)KGK!6ub?N}f N*PgC^F6*2UngHUKN2~w< literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/fi.png b/lam/templates/3rdParty/pla/htdocs/images/countries/fi.png new file mode 100644 index 0000000000000000000000000000000000000000..d408743df26fae9ebfb34208456ecaef616fae24 GIT binary patch literal 1040 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKYQlf!Gp(+ z9zFT-yZ3J0x_$QSxkHBzZ{NOyRWMONI$Kev($v1o z!l~87w%RRVrjvJ%wNq6<)O^42S>XxuvWnJL)i0VnW7n*CJ6Ekc%`TcMt6d?hU2f)B z@95JT5Hl}4X+d1(vb2Jgb?v*-a;LU*t;s3q*s%4=@--(nZNIwr;B8jXWNzs+UYTqG z`CJj@97&B_8O?kJ{c?HTVg=n|b(2aB(@I^NW_`O3EsIJ6n{sQfey4!x7A|#mULB5p z-A?{JZb1|MV-^HNPl(T27#iPORJ*C7d0YF${fk$goHlzii&!#?SQ5KLDyMWRk8C=R zOe()(jgHzI^%g>C-1qo;-Z`@ZP<9H*VaxcJ11gD_1UEx^&^fg>&c5ojrT@%$YN%PMtb_ z{P_O;`}geGvwQdMO`A4t*sx*w^5t{q&Yd!4N_Tg6eSJMJzJRed3aAnSp_|t22O7kf zrYYv7{*UoTO+HL)Vj>A58ZRCb#b*?tA_KMHMGo@W;mCC8fFSci8wR^X9C_3 zfn#ul0|h^QecK`KPz@T6&x4T@Z)u3GiM=yU%<&-Omge($&UeI8s$jbDS=iU9y% zO=1ELAI=ToiCh&C_RFKu^#Bk7Tu4ce3**w#l8D3A@Wt|a!Hj*ZiQPa*$uukULI$5r ztg<3~pb)%qHGjn7vKs7_#QK+xnF^`|?ijg6;RxFtByuhOQWO4)pqQaIILGt&O6`j` zDvzHiL`ZGg0+Eg?H5IpaA)aZ(=A`MJ6v#{#BZXq*zSVmRM5=p6%VfqDm%pyhRonuV zv_c~HTdGje=0w|vC*|YsJH4~4PQ7X(SRsPpv3ITgBTZ@}MKef}nhGRHfmmA%nQlt; zMUNgAfx0qfZwX|ifCy7RL|0k34wRvNLJ`QTjdqq{D6h7W-Q#Q+@D%YS4OTZ@im)_& zO+(&0U>8lStkL!}6~+okCwVb*zsn*U^S9XC9NiPx_g?hOt40IhbD#EAQ0|%TbWa&y ze>6B;WDue2hsqS)bhx`hZK>7|FuN>;-1bVPsjhoajs|)C!!R09qJbi*PBS{*uGV;F zrV)c=*rghTul1UBe(3aP-MM~?X-Y{(od!4kKu}CjbxIv-SxCkYvdcovtal*4H~xs{ zTJpC_a)?2CTA2NXUH*nu(n!b*s6aR)Y3^R(9P9Q3jo4Ge_|dJg#vfxI-1Ur8Tbh($ zxI?qcq~VIgMgP~o9|)bynhI~15t8F^K<&!tgfQ3=ac9zLleswq%h7WCF3iGw^6&{2 zObRv2mRASP3yu=MkC(5$I~OCE`~;;i&z8tbzn%Xe^r~)-_$EE*^;Osn{v46b6|-H_ zkacmw_Zl0CC9K~h+PlN{98zRuPQBu7!C-k@V{8VmdH=#9{-gMmVE4K`ja=TB){ETP z^_$bOr_-Xgt;HrC@V!K+i@w8&F=^TPQ3L?H&sCa1U5Gpwo1DsdSQoo*1!iOZakKeX ljXm@7&Yifp6+d$q*8&}156!Q{|IYn;Z(>|3PIES=>JNm|q@Dl( literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/fo.png b/lam/templates/3rdParty/pla/htdocs/images/countries/fo.png new file mode 100644 index 0000000000000000000000000000000000000000..1e123760d5a3c910bf4856f627b4749c0f4568be GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKgz**h?DDp zu;@V<#Z%e_`*n@>=$qcOb=qNVf7sFWsei~7pMdLuVXvbTp2Z}*$jp6FP_ji?Ymct! zNn6+J0pWYR15XARXo^SZ^?Q-0Tsw+QMa4|NQ;4apU?we|~@c`sL%t4-X$cxOeaF?c2A`ojZH>?3u%d5AEE! zqr1E7_pcvkPMcqdle}4b^`Tg71cW>Xkdimnzi|2Rm+`fG2;`wuDk25fwW@J3h z%6f*K{jz|-86lxNva(my)gL=Lehv@6bNlv*U@-@API@}*0cE?&HN;lhP;V0WB8efq=+knY2W5AWW+d&iC) z>({SevSi7G2@`5-Yinw1fKdsI&{05q2uR&iNCT>7O!9VjVd!9$^#F4Ad%8G=RNP8V zIKY&4W)1(vl|kG6XWzJg^WLQ!3_`Qw#8*YHOqpr7aQUvSD;IKVOc9zXy)JxV&eR$6 zr%#?baTc%0rcyI56OG`4k6*TJEB^Ail!43h^l8rm5o7M|jxNtrC%ihDTU%HvZ$@&> zjJRo0`GfVxj-3roW^u><{c`PEm#3EYOl+ClJBNicel!`p6%w8QqGQF39W6^z76iD* z`%Va3xhi;>TXD|&C7Nvx3)gX)9-Q?wi&4@y_wLro%l!^NlVxb#X83Wt{Myx^Q1Ep1 Kb6Mw<&;$U#{H);s literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/fr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/fr.png new file mode 100644 index 0000000000000000000000000000000000000000..39bed1abd448a0c8c9bc7895e20ca63578959231 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzhG+i?(Ihg zhT{wj#~2uDn3+#9GaqARJao57)j*-z0OUqM^j*qOY4_jLu_V9S&=5{e4;B9#L zM?b%-adBTGBcGgTL1AmeFe<>!``AIFcMK79E2p+m>6U%!3+ z{N;1!F5SEL@Yu1F@85s8ef!R@zyHo%zI@{B*<+_qzkK)Z=ik3yfByXZ{rmmLkFVXj z_4L`ZOP9`Hym;>P=@TbU9((iV)x(GPZ``gXKS(>tK63-sGbBg10`2IovnP8%D4wzK={ z8cQ?1&_I4L+Y#ur}{q*ws@jsQGVN+wXIBwFU!Mr&jjr6_HiG^B&xqFfTf8601ni zmEft=E~>f zE8`Uk6_jh1Rj89Un5^%-$jP|MC}6dJ;EI5>jj3#T3|6yD?c1#Lc4e`+GO)NXa2K)i z6tIfb2*}k)sCOyKCP>;&w(wcu7Pc-RW=*(qHKTGBqjnRQZUe7EIG1Iggv&&AuesU| z)mpJ@?XtG}Ca>~HS?!s-J+N?nWbOWx14kJ4A7(ysg8j&Gp>vl+?%Y?KzfkegRsEN5 zt*+g)`~1!8#cQ8W-vUpcb9??O;Omc!lP4IC9%VXwnCbK>=Cfy5Po88wb&~V+X|Btc zc#j?9yLv_7+BM1Z=Opgkm3{a?@zOJ=S^sTL1k!)7^V?PMk2fc){fUJ&R+< zj4oX?`|!c;`*)|ym#u#NbbIpH;n5?fqerZtKXd!@$N&3xzdyeNo<8;b{K@b6bFXh- z1HOC-`usWk`}er--&4PRP5t{P3mCt^SRMsfL*U4i)mwlPj7i?^E({&4vK~OrK~ERQ zkcwN$2?v<&+^LO?Vg9?vcCO8ynmK=&8=TyxmG!9|yY_5Znpl|Gwr_k63pcE2IdY@p z!h;19KAdxN2phFx4nhi0zK QIRNsqr>mdKI;Vst0PFHlDF6Tf literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/gb.png b/lam/templates/3rdParty/pla/htdocs/images/countries/gb.png new file mode 100644 index 0000000000000000000000000000000000000000..30035dd8cccc376ee92ab66d1d92883f08c44ee8 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-;zS>vao*?ZyWY`br<>nKXRi%z-Z|R(2mC^h1V>$rPP!7CdNDTj zTy*keFaPU>6?e;OFBg`bs%?BWcj383OP?J*eqzVYS$q<61Z8FmO3xFPog*weS6p$a zwCW0Z&Be+(^OdxhsT*$8Hr=XkxkAVEfQ{Q`OQ$2Qe&_rnE`-D$@DE?^>T@_K>Pk%N z#pLX3Sw#mIEPi+L%;Q5xk1SvH>eSh}USZQ>Qjb)%6*+irm@)V4<{hsu-)f)~hU9)Ze(yO}<-8yjm*_q47Ht%?I<6d1v!K{Xf{kd&h7jE?RuU@oxOHgQu ztY(2*K#!)LS9rp#l*WMux!nVMJta7CQMDvozO91otb-WOwPjG@|m5J zwgab@ZmY0@=q=^+4K6w_B{&Vv=cCJ(w4xJbJ8c$R7b^NcS zWaqJPZT+T_=|A_5y5c;cQ~MKws(Ax6bZq>>-BYHu?%Xh;KfvA|g^RAyFqGQ6nKyAtBKuE8C{1SgRnPA}!serq-ve z-KegfrJ#@v6x7g|q_5wkrIo9!T%@MfX`o-Cp)t+Wv|d-YTuZA)SGP`2uUc2PQb%W& zl~t3GVS|A|i?Q(p3yTgj(>4>6d3JV-oSb^Ctd_dDO|rLJ;^sEZ$!WQlSDl5$Y&W-+ zzP_{E+*bMf&-3)0 zO3S+_Xpi9Z30qdRMmOv?4$x4WrmSTZd!m? literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/gi.png b/lam/templates/3rdParty/pla/htdocs/images/countries/gi.png new file mode 100644 index 0000000000000000000000000000000000000000..afb539b3a51743292da6d117ddba2a6f930dc483 GIT binary patch literal 1039 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFuM`r#WoCZ9 zps?T1|Np=LSFc<>e(d0u+%X>pW zU?MN?4k4lS!ors%Bp%Aju9uK_qNsR9L4Jp{^m1wG1Bwb86cuO4%AVKIc&D$wNm+S| zs_GsMjs03$(-akt>*=-1%bzkZcxYj9)y(vgiOB(D;|==yPo11zxw-ud2>9mbcgM}G zPfzz}K)@U`vkn9OGeLoWGBf|=<-N?wIg^pGB{A_rN%4l%)V`pgkG(xR^YibvwH~Rc zIN#X#puhj)!UdPQySGlCzJ9@iA78)z{`vFA_wRS_+&O>l+`oVS{{H>@=g*(tzkmPw z_3I~4>IYEr`?qi3zJC4s>C>l=A3wf(_wLP`H?Lp6e)a0r%a<>oKY#x8>C-1qo;-Z` z@YbzcH*VaxcJ11gD_1UEx^&^fg>&c5ojG&n)TvXuckkY`Y18uM%jeFWJ7vn0?(XjT z`g&lD0OMs8&>;kN+veE-tzk^^c6VXuV3qX%a`t+vSa|K&q9=b|C}^99oowPccEx5vK(J_Zp3{O! zyH*#~qmX9O%-zJ+OWYkf>mn5SughFx#}?90w!S1%|wXz!h{S59NfocKWP z6|0x!zkek?J6!Jow@4RH>c`JUNn)a(zj5|)@N+YA-OM=QeR5?6cc;Z}i(8SiBpF&u rM3$yZG3{+ty)!pw?OES18!FiuCfVsqU-%ui3>0{tu6{1-oD!M<@5o!p literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/gl.png b/lam/templates/3rdParty/pla/htdocs/images/countries/gl.png new file mode 100644 index 0000000000000000000000000000000000000000..66cd000ebff6be325db2af8f781aed5c991a1e4f GIT binary patch literal 1038 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF{}~W?H8|wj zy7kA7AAj)R{>zIO&N4EbVPH7R$asd4@feS@OnCS~QFYHD}&_3s+! z@6piMucdWFSNEWf&T&1xQw9cSjSL@JSX?zTy<}o?-q`q-mDM#Xt4}V@5A5xpJ32mf za(d2y?fK9 zP0N=rpF4N%lqplXySwY_>w&QYjH6LNqY&^=kNpEQnlZ`S-G!lpRn`N@+2iTr7*cU7 zIpF})u3fhxgBSm8o;+JROUvt1k%N=0E~nfwBc~w7ty7n>_Ov=IJQr3b?#(Kgr+4wf zCZ%J4f))fs&z;}fC86Kmu%oujFOO3?KtuazXn52N{{W|R7Mkk1w#*t&-ipe}iwd3; z5*B~`?9Guk3?fBac(QZyR;||3_S&-5;L1f7uA3`vnVMe@pDzCVN5o8t3=QVi64jq~ zjUDy4Hkn<%9u&;Nb&{*_lf=(9mMWu}uQOL`pG^MD$8f_$&sf1olob?Sp00i_>zopr E0CiPi5dZ)H literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/gp.png b/lam/templates/3rdParty/pla/htdocs/images/countries/gp.png new file mode 100644 index 0000000000000000000000000000000000000000..e4a6159dcf0391cc939d279440e372605277a4ae GIT binary patch literal 1030 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFziMiB+{*fY zdPcpq&6)Vbb4kf(Q_`+w=S=bQe^^mK^t@{Ih7;(<#&ctyuGG*@|zAmwjKc`b=8(h0KC8>3KIwDlfHk zcJyF*9y1VIhZ^ygI^>;eDo==|nanY5l3TO9n zKD=acX}ZLjddBCA9KLN1KUd28adyCsBK0#7tp9tn&L}gS6=b|?uYX=z=vR8&88(Kq zj0|TOfF$D?M#gh2%x9RHFR-#+WM{v^#dVFB_Z% z@Ml24$AEwYh7ML)4ihBa`!H&Ye9qO->FAmrR;>Z`qob z6CFFQoat)ob6XJLXWs1}P*f6;9cqBP1Fo+_b*$zcN>q!74b(e zA4v&_&Yu>0^G@XX5FU|Nxu3p%<7klz360_qd3B1rkFQ-aByIw?$Rv@lpnwoD!O4>* zP7a$WAkNAaIZJBws+G~VU%#@vm32EKE8#OAgLaz%zw08O$)K?DboFyt=akR{0FuZ& A0{{R3 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/gr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/gr.png new file mode 100644 index 0000000000000000000000000000000000000000..1fb9d5233714f4d45d55ec008f86399685e405f2 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF?`WUp=&;z! zbBDFnr0A%X6DM3{Ufqt&=P5~myqa|km!_>njkAXQ$=N#zW#D^ z^OXh$Qw$BK>gsk{SS&U%ndIcO-OOy7o?eZ$^*m+eSOmFxRc`Ley1M<9mD?&Rwv?5v4-T&G>$}+7 zd%mh_Q-A-twzh+{wOcwm4mUJx&CZ@ackZo{l7-Wzoo{d7HGB5e88a?Um~d>ugd=Tj z+v@68FIjSL-n?temff8;?fBHG$JVTQxN6n?rAx2RnR9B*nme;+@0dGx*SvW^v~$_A zLkkw{Ua{ivf(5%aZ92PU%ejLG@2p*Wbj_M0hY#PGG->I%b1&x3-FV@`%jL^=U%mS7 z;>DMjF1_5i@#yvI?~We5wRi8){rivY*>kw5sr}Tci${)}-nMPe{`~#u(G&OYKYa4!X+`D6B}?zTc=`VMi+5+wU0$_%Eiit8u{;W}hJbSZA2Xl? zW0JSK3quF1tOt;D(9^{+q~ca`!T~0WC$q2e>LlB~S(`hz#%a;x1}Cp|mz(Yie-4@) zd}(uW_o-8jPG7mxeCG?P95{SdUFX!IrK@>GCVhQpm{>OR;@zv3g>mnn{9Eau81`)^ z*PkS@XJLKw{!I*AxX(dx`;wV%tBf8MWmUB;OFNeH%VFWPZEd%9%{tci?b^M4oS)TA z8Cy$KSa(`fwz@0{H8Gtcx>TjXDRHxG&YL-VnVug}KhomP@`JJU)8gh5eSr6Ku+-`A*+sS2e?X;#Q~nd2TI>y?fSrv@Z*qzAJpe5x>55K~uJRcPtB^ zy+2{irRc@SgC=eA?OG8uaYM$oyFvZy!lrJGnzbur!n(MHhx}R>BrQ1_Gizt&+6!qb zP9`rolDGLr!S-84JMR>3zg@WFcHZWznX670Y`K=Z;bQTQ+r`^&=B>X_w(VN!mdg!? z9@p-@Tf6&K?%FudwPspi|JwA8 zS7YYw%isGrd+W{g)o1cIUrCy?BWuO6vzM;ePgv_cW3zGRGLx?5=Dn-zrf!Z|y038C z*_PvXdM`YG`tsHJYu8R*xN!0M_4`ks-hTM-$KSu_E?oHf?c0g7r?>9hzW3O{h3l5S z{rGP4(e0BKPF=ELSyppS-Q>C>mk#H&CDrzoE!aFGy3o6@Eh)Dl{@}^oTMjMno>f!X zlv7w6-_-8EZbSXeo7ayXJ#ykm#IYkSlPA@$U%lYpzkh%J{Q33k*Uz6nKYjZ2?%lg5 zPo6w{`0&b=E0-=^x_I&8xpU{vo;`c!%o$*u0%LX*5DbAddz0fpMT|+_?k)@+tg;?J z&OuKX$B>F!$q5ITB5t~rT;g7Kc80Nfe1L+3wT#9QwVW4ory19Nn-jTlQscsDVh)P; z67!b zGmQdH9KP#evTn)R_1pK&JbEr9oKxdTmiF1{5jSoHguZ?+wo+V)Uu09@gXdl+UgfOP zJ6p_oIcOpymnK(Xu}Z4nT$_dW_1&C}J(x69P8>ctZ;o}U(&HsO$(E6thB@~b8QQB2 U)n=|;r33Q5r>mdKI;Vst0Erfp%>V!Z literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/gu.png b/lam/templates/3rdParty/pla/htdocs/images/countries/gu.png new file mode 100644 index 0000000000000000000000000000000000000000..42824383950a9aeb0d589503c4a2e70328ae0ef5 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFZ?34CQri*a z5mgY^9_f`A?pN#QnURy)=joEBs~#5~)Ma57@9f$jARZy9lB}qcE25YXlC(frEsa4U zmQ^lE-M-N-sKeT?kx4FvO(B6@A%R0Nky9y&TQ!A8J)K82nO`GSSSOoT&Qn-BL);)w z!XR75yj;$-K*_pN)uvR(xysP9RoA&n#VpCxw^P?X&(y2YDyUo6KFdCQlCfKvk!y)f zaF?ZTy@gM$xmT4-^i=ownPwiv_Q9=o0d;QCeJ&AQ?lJuVY4hD<`n=;Ohh#4b&RpP^ zJl!{GYGB%|u$)D~8FNFk=7;Aj49%PqQ?M|xY(-Mp^7NW@vH5c|YSv`dtjVcgpINgi zuWmK%808FVBhk+rP={2418n#+#Cy@>70|vSJ(cmwqSzmq(xiyL>XCUZ(F~5>C8mtfECLY qEm^Z^*GBDHr~LG2?^;>2_Y4dd&l)Zj^PW};^1i35pUXO@geCw-eh`BI literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/gy.png b/lam/templates/3rdParty/pla/htdocs/images/countries/gy.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d0e8e52036101ebe941d1bc624d73b20e46c7d GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFH_tV5t#WZI zbdBkZPMMPIl;aTG78qFTA3q^Bd19h|u$F0*fqATvb)uPViiKl_O>9SaVo!A1q=c0I zxQr=D_Gy*@jh=4#4$fJ&9tBR`C9YmYF1}^%vAtn|)!vB{BEuT|qgsN~rzT`gPtKf{ zlr=5cHODTa+b*C)C8o}xY`RZot83%Jh_cB+9m^9M=f`v`OFFZj@7zX#GaCfXZVxD0F7QL`u^vX8z zvm3;&?U1^>P3p#Wx$_(3?;TXVv`yvVVXX%Tv|gMxetOdQ`Yz+w=PW*5b^LnM?bm(( zKM#YSo%Z?vB>Lxr=&!dE_Z#r-H{?HLDs;eH?1YWfF&CAK5!Mg$gWor2eC;jSD=oZF zPVBIb$}wZ@^G?=Ryqs?ZdB4m^yqA&mrZm5lmt&6@U!#QZK{fd$V^>r@0x$Jgx zc@i7_p&;*dVLmWUfiXJ@2!_ByYfe9)BE}?dcNc~ZR#^`qXTPV5V@SoVn!Wn>C;uBS$?$N&L~>3f}6S3 zs!IKwR-fTlzO7!D9tMeqGI5pqubLewk|UFn`Fh21wM9D2B3Uoi&y9PKEhPE=LDB(T z6?IOLSr;zb>7GAtV|Mi9-HuSlC;^Qt;dhNp&CggFp1pc{LU34@yhccHq@AVxEj`o2 zhlA(4IyZ=GgoIu+S!!@l(N_Dm@ATL?{p_>L8D@nUb}orq%ntIrr>mdKI;Vst0IY6E Av;Y7A literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/hk.png b/lam/templates/3rdParty/pla/htdocs/images/countries/hk.png new file mode 100644 index 0000000000000000000000000000000000000000..e5fa24f60040eaf5ab12fe407889ef8fa9807abf GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzj*TK=^dNS z&g$E*$goe5p;q03r>}<2u|Yb$L(d@*dLRZI<>rcy{PZ6M=&!U=o@b`G z!A|XxxBg}ajiPlM~h7T1C;PWqXg@H08;WBew@ z?v%gjX@9f#2~O`4oGye|ZT2wwkm7bH+TmKH-N$72JJF7}W1RK{TJH_C{*vi?Kf&!u zisz#wk1yH&pR@g6r1`u^_xYR?@H)fqb*A5^+`x~yf$y>c-em{8%L#a%6>vJ%?M-3$ z>w@qP<#7)RB0txpysu4tSDX5+DeG%f*0-fo&UDv)xO8T}0^<<_v5RIBXC2k=yK8?= z@p_f!_cbf{LuTN&ypWS|UMJ$cUl&Dxt4Q%}>wE++3ooX!V3e@^oMoE&hw zuJ~GG#iPY@F3y_#bj`B6D;EAad;Ii{ZTrMn4@hxd*HyfE`qY`-yU(0C1B^jnJdOe~ zLZIyLf+(Oi#w2fd7lsa2Sq~s*zo(01NX4z>gab@BZa$5Dq}+2#ebdJdkIY-Ynp%$J zCMnA<&3?6$MP-VPZF8g3p{l@(a+ynoyM6{=J{!Y)x6x@;B5P=CrRW)-Ue}qDjCS7~ z6rb<-aO%~{%MrVFO%}5HGuNlV$#7!XrpZx7x2>e3Zad%6;c0NXD97b=Qq#6}t8eMe zch`RHa#$Ep8hCEf5zW0n|61Prw#+KaLDAVTF6`Z=lV|xN>->)0lIn6$l(Y-ubM#Er kUaw$Y%&aSFnRAbk;oT{{t~4e-PEc@oy85}Sb4q9e04S46K>z>% literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/hr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/hr.png new file mode 100644 index 0000000000000000000000000000000000000000..fda732810a411de2c34b57f12b931a98886e2924 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-{a`KH#UBa zhtI(!OV7IaOypNs=o{Z`>bYdW!XoSNNNL+RQO9G;_Jpc?yQu~=CG-TD#Z(m?aCcnI zz|hLd+R4h=&d%P>$=S)z-z^|8NmzK2gv3N;<@u_rGZhu<<>f0hG&)R7*Qu*F>FG_< z)9bOYSYcu^+thTLvGGJ}>-kPjYwYZ21Oyxo3OeHFx69jmQ)uV`Pp|%n$dv(s(^FD6 zsH!j3H=Jl_JkiB%pI_j?@W_QlB?n6@j+R%SIeP5)+fSd)UcC78&!6wVetrD%<=n-K zr_P@J{rB&$KYt!Reth@-{UgVY{rvUo%h#`mj~v;vZ|~(RmoHwrc;e)VCr=(-x^&^s zpWolVfBW|B>+|Q&E?v5K{P?lk*RLHtu>Z~N8z-yEjxSsCq&WZi%H?Mm80r}qY8e>L zva+6GWxc@5dsRSSf}r3TNy)pivJVv%XG==H)YtEnk$G=w`q0ASs;TK$C#M}oM!($L zp1Qgmv9|u@@Bb$t;A=p@S{s{h;o%>{!hXcX{Y*)D9Uu2HA>ngs>YuEvuNfIF! z$q5ITf`UG4MP+qwx;*)^Q8D-FRSr&(OV57Ine%7Q8%fKbs#C8zENq(ft&>luP|hx~I@(w|IC;T@ zU2Ar%n6YJN$JTj`eQm2IF$H+Uc{v5S`Q7xoekd7O*u953$$Ufz>jTpa=emn9@_NJyL$6r3O{ds|WQfxP^ANy%C2 z>X)^(pDHRY*Vlh(pnu!c^sSZEXDzLD78ZBy>^?g=eKj)L;pX1{l{wX;0SzO%L$jGNDDL*pO z|E8vX&dU0&p?z-7+-ntGJL1!;D<|!muwiTC%vsrGMWJzi+c!)+wr^o(lK-nq$Br(Z z4)N771_p?ijxjTzVr4zf%6f)_1L(1fyu4?5c`pkHoDvc`DhYiS+S(K)QAcR*M7jFI7S1B1I378lJ- z&zYEbslUZF{?iPEOC<+}?V6zV`6=4fJF{z&jtGUx9%i{rtWL1pEjJ zdK(b%Cne=iV?8i(fl)jPaD~7w6R$R)9AlEVy9+}HtE>l*bI{YpF{I*Fa>4j8O!NA{GHdIu%wz#^yo`1mv zSBHuPp;MfEVoEmju3o?0IKU&yFU}{#$-*oUm>1o`yks;~G!7g*du-l>DN^T;o;-j2 zG_%Mo^R3G)c5B&Ms^+)~CIeB-k?$@g~_AoQ7K5F=7%}<42AYXgB`njxgN@xNA D(U(1^ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/hu.png b/lam/templates/3rdParty/pla/htdocs/images/countries/hu.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4eef1c4f20f9e722f62b37b96f857a0befad07 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF|M%_tvIPsT zUb%Yw*zqe@uQKE_Fyt^W7csLIu(IZ}a+YxNR`Ci{2}o2+$acueH_Iz_C`zYGD-|lM zH>fM-DC*D9*KE+#ZPnE%)6lBcGM#CvSFdN#Vqnr?Vlm0WX}Oc-1S_)+Gq>e#PBWa` z=DYc?_YYVT;J4h*YrdCLlT*O#fY^1hfl~ux7sux8%E{i6UAVU}Z)@J?Z=avNe0KBB z&Euzz|Ni^?=kK52e|~@U=GBW=FRtCVcIDcY^B2#bJbiNCfqiDZre58ir_P*w{`}b% z28PWH4228~1q=*ZnVEO6vTkE#-O9p0NvTdk;<~>6NnPFBrl#jCES@+ytu`{cW^aGg%IdkB+a*WGZPwPW{QX}C1T40& zcp4D!Brxzvc=(%ygy(T_w<9C(Mn}I)PJWh}`YtQ$Rz}8~l9Drr4qZ5Y{QR+Frw<*v zbNTYEixec15XMcSE{_o$vKY#xG`t|GQ&!6AE zef#w3)4O-?UcGwt^y$+lPo6w{`0&P!8`rK~yL9Q&g$ozXojZ5-?AbGC&YU`R3K)IB zNE`*^g}_9utR|pN#w2fd7lsa2Sq~uRfTxRNNX4z>gab@AHiv?_HYYLH+}ZQjIeF5h z2B&!kKeAe$Ox*V_u1+zqaHXWf!V_N>J-TzGW5Iz9FIvvL`D3#{pjbIL)-|%yGc&a` z)Hm1JHlSnb#=Yf-rf3TPuCj`nb$houL+h-l*qdtpGq2pgee=|fYe$VlHBvH@pC`Rb z$WO~kN=(gt{eVNn=vv@3fvF*vE}0q_UAbO-jg3q5LveAzN6wQgGBQ@3I_=fT*ve9Q q!?Kd~i0D<*D|4PKac$XH$QfOi*czo^D+a@ z)ka*)jCj_W@~$x9U1=`3++1**gG80K;4T;0%}z2KT;$igDR1>wKN6^YB20gOpzbtR zt;@-FSJR!&B|9weGdh~&xIN1DP?FPyOwZc|zNa%i?iBf7$o9Ea9C))haC@TLu`Hi^ z)zR0=BW~5kohggFQI~M9Ddkpk>f^qm%V$qtI&PWc7-3+&b z(RS-XtZ!y}-6{yURTy|NFJMEo^X-!Gr>$8JnlnxxIdt#()i+Nc=ZZ4)n=3xf54dvX z)VB}s|NZ;-_wV07fByXb{rlIiUq65T{PE+*w{PFRe*OCC)2ELgKfZhS?#-JwuV24@ z_3G7&7cZVZefs3dlZOu<-n)12_U+rZZr!?Zb9G z+uensgH_f8$l34d;uunKD>>l+)1=rxhaV?7*W8_ZXU`ud=io*sy-QNpp3N#c^v5a5 z$SdpDL|tiua6tdw3SNmjpfK`u^>bP0l+XkKa(8vz literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ie.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ie.png new file mode 100644 index 0000000000000000000000000000000000000000..1943e6b80d1e79de42b390089a40b19bec3808e5 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFXQ*aiC}&{s zWnl1OV6J9nu4HDdW#z2rSg5Ggqov)g zt=F%oRikCtXJ|3c!gQLc#cT_sRwJuEYrE-o)_v9ozAZd%cUgDrqUp{^M@b2E7Ti32!x^Vv7@<8bg5sI6l zm3PLg?Mc-5RByUJS?frK?!h#j<5_yAatzMq8D1(hIbUFWwb<-em(z`M%Ucyz4{GiH z^tnB4bb8h5_J5M^yH3xC4X*zu`~RC7@Tu45@07rA{eHhE2mYKC@VYnX>x_h}?V*1b z=RBR7{C`F9rv-VxSCl--QhZmYe=5`9Ql80=Cc6h!cK>?3-nDuDn;7u3FW~>*KYxG! z`t{@ci>FWi{rmUl&!1nve*OIU^T&@LU%!6+^y$;Pckf=me*NObiziQ>Jbd`@-o1Ob zZ{NOgvvC0Jb(BC0ef}f*;5>od6U=1M$MZ(o7?O?1A{QL V!SlaQ9Y2D?$kWx&Wt~$(69DhlWjz1@ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/il.png b/lam/templates/3rdParty/pla/htdocs/images/countries/il.png new file mode 100644 index 0000000000000000000000000000000000000000..a9161bbb8e38f8fb0cdbcf2533e101aea993da2c GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&MLS-~X#ut{y*j zyrsSS@bS}Me*D>X=H7`L&o&=8&FosnRk)A8WS>ygaiP-vA{B>3BD$5EPAJzMlnJab zY&oc1w9K^ikZJ1yoBlIqwVSM3_qZ>7YFIGOb;ebPzT=L4$1ST?`OLlPKH->S%hrhX zAA%R$jNSYxYV9lkX{X{hy$_vxId1LqxK)p17hlib^(AZDyVNxg%J+ZiI`OS>&6%c^ z$0qH#w)*nN*;`M{-F9-#;VY{TUOI9A<>2XKr_ZlixAE(*f19@Lym9CL z#j7_?oH_sM-ADGK{UYW2=0)diCI;L(iW-d+^}?*|TSUeE)v;&Yge%{{8*?_s^d{zkmP!_3PKq zpFh8S`}Xzg*H525ef;?G-Me>h-n@DB>ebVyPai&fc>DJ4TeoiAxN+mkl`EGnT{?H} z+*zPOXU?2Db!z$Y<-j-uM&&3VH3U|Qoz(&AVNCLNcVXyYmGuB}4tTmahE&{2PB_5i z;_@h{C-|#~OLLRz)RIZP4NiBOf;>3|o&MO!)!b`L3^a653_Gs$E$I45v1!8GpEl3! z@@{attZr0#<>pK`t=Wa!u9|NNXKsyAfU!WWAVWP`PSt=eC8FZo%pGzN!H$_FQPx)n2ZDcbVPwyGPfakx)5zxBY$ zHy=O!`uFeDnKM_eUOj&1^!MMtUcGz!;?3(TH?E&Nd*;{QKl=_HxOVHtsdHyeo;{tn zB{^wTaO<%$*GUH6)6Iis+J{f@%vv3hu{ff1Q%cL>;_fq5?vsq7mbfO&4CpvrYCl2V zvQ#m6u7yfDgJun@W)-V(JFiKrfMu(M(-c{k$%>|h(sotKZY^3)bsFJIOg%gF0w$Qm zFLX*><(9uKAbW$KMh$~j9kYG|XUGzLzdnPIX{KqLoH91K74Gsc*cR~o`Li=Y4E4qg z7s6Q2gtFd95V#V@e=b4jNxtG-Kgk;f+OMnhAJ$lGO49k!<@l%H?O3t(|7ijLrUks~ z^?B3d{d;=I-^JBEN)FI~EH_UzeHr%nMQ6d0|efS3@lcIrI=RL7X)?e4R`O(F}_Qt{S?%~*ov?;UV$p^l zI%R%&b$n*WG%rV_F}FThk}9H_(ptHr#YJ_g$do6IPOGA%er3I4(>mo=wae;O+bjpg z;B0Q=V(sYV4CVac;_2?f3npybzI^)9-v0W83A5*~Z{90w_MU-Z^+H48^HXQ0f`1AMQ$JA3dg|tbFd`#Yaz`JbL{2+pnMRK7Dxf^68$#`;J{Xx#!mY#oL!I z+qvS{?Gt^QC+5#DN|>6EF*RfQuIUSpEo@udHhc5za}UoQzj1v1?)5tk?s)m(rM!at z?gP7De|Y`s?W2Qhu+GNjQ+W8-xVT>_ zDX-$=U&O_IPEGxuk?}h#>uFBTvHkn6o;!Ey$dM)U=f8XL;{MH>7tWmd@#Dw0Z{J?L zc=7Dnvx^rm9y@ky&z?Qy<>f_1MFj-~iHV6JAt3<)0sj8}etv$wzP@g5ZmzDbPEJl% zR#p}k7N(}ACMG7v#>NH)2KxH?dU|?VT3Q+!8j6aFva+($($W$V5&{AOyu7@eoSdwz ztjx^Jz<314<|rU31iVu=&H!p)O!9VjVd!9$^#F1Xc)B=-RNP8VIKVV#4)fzplMe5_ zBNJ=;*Lkr}gVVWt6D2h-GQR86le1G4{MhNBxNJ$zv@bUj?C&qwAo1+V8IuJ8b%N~t z;c|y>NZ{>b0sl%pyX% z+UqwM7;c|rXl$grb=!ItuF&gSKYl9x*v-Kea`g(A=~jl;Tk8JlGwf2G+>2|Iv(xkc wvn-hK(WFf>SpC};vn%GeOTT;i9eO6q@HgCG-Zt&I?4XeFboFyt=akR{0Kk`6=Kufz literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ir.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ir.png new file mode 100644 index 0000000000000000000000000000000000000000..a6f49031fac803d9bc3ac2e60aa3d29b21380f83 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF{}mW?x}@}S zbl6pedieeD)b+qcT4%i&vztKNnT|oGT@VKpUp|e9%x2I<9%LHf}x9Sbhcd-dkU#mncvef{$N z>z51X&K^H{kUa zbGo(l?fm&aR<3xzXwi?=tKTkN`gG~i>&uot+_UHBw{LG=zI^rk`H7=P7cX4+_Vw%A zH*fy>`SZv3@6Vq-yL{==$rC3|96$c;+qbJ%ukPKu7Z|_5SRMsfLqPn={TDz9#w2fd z7lsa2Sz!8PIOyr(7*cU7IpF|PNNDexId6PQO{bcc@c8JgX>`h}de--CT3eLXt6RU0 zrI~GWQ1q9-u3#^ouIkFJpCA9f!C}FLD<3*1JKeEST72hblk?$azU*8d3p|R8J2x3^ z_7O;Y{`Bz^b}r72lU?2%M@_D{aG&-*$>GJ+ns#oMt$9*cO#S>rJ(ZK6pO0h^d6kp9 zYW?cmoOkbDz07@`kXMWv(+xwXPZxQX%Gi2oa?<0;mp6H9uh#0~Dk?VG bSjo;1`Om2IOK9Ojke5AO{an^LB{Ts5@sf(A literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/is.png b/lam/templates/3rdParty/pla/htdocs/images/countries/is.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce986449682806ee8b44740aa210a91a86b64a3 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzgn*sw51P1O42-q1KdN?fXNLbk6(9i?H!FwVj zcT`vJC@)`HU%!6Yvb(cqpIf%XlMrIqV?N*yx_IGt3n>hLG ztl3vOyLPW!^&qckLwWTsd!Hq)K`Vpew{aL&EI4|#6Ma5Iv8W&xhj|K%DkBK@H zANQc5>|JNa`Rd9?JzYmCD}K+N^J(S`VEh7Oc@$s`0ns+?k3b2=ByV>Yh7ML)4gs|% zH9J+#!b;v+>-7xz3jz{dmnJj^MCQfSIR+MHdRhi#+*{J=6TIyFjGOk^-&%fhKRd_B z_4V5p-`O@>x2@Z^aqH&QirMrrwG zdHE&<$s|eD7FG2Qb;T@2tx_$EsTLNKEsW}ntlF%t+pRqoc?7HsaGT;5usk4qU3kdy zkhslpkt-rocBG_iOUc-kk-8-{Yj;-Wj?9t+C5KKOI(YKnzGM6L9^HHC)}@mdPwqRq z@ARe9w;tYl_2HF4zrM{BYsWbbX;U6v6woLH-c3o>0UFJiM>7Xc=$*XJnxo_RMX*TaC?D$f# zV8WD5+q$+UPLGXNE_Nx&@=^)#a10L(JG5r?-j#axnueBI92zOB^OV&x?p%zCkDW17 z?6e4j$fb}ePo5cPWv*DMaiWj^^LIur&B%|%AEZpKg-sJlb!Gd#lesme#Uhf`=k+Qt z?=EAeTHBZgr$sS3ZW%V#3*2}M7z9gLg^F24OE^{PB&=uZJIpb4o@(K{!YOE#TjU1+sI>u!>%wI# zdEL6KrE9p1`xTXHnT@(dJbDdMw|W^hGgx-Aw;zcMo+F#ISfXUJcKCGG)*Vhwng;M@OMQf816q*Z=?D_stuRT|2EleeyZD z-|qFRfTALeBS-uzD@|9f43v`MojAcaGSWCZ+bJzAKtn^($;l!(*we_!(7-_7-rmm9 z(P7s%h7D_&uU=Gq_So*@yOfC&A`{}h`1u&pQd6_CvQkn~;^N}M!@~mu0|NpA{Qdp? z{QP`;d^|lpJv=$dCrAV@&dPcVXyYmGuB}4tTmahE&{2PB_4{$42)GYlvvdQq`pu zCc2Z28l8FtIs4fDJTjWJ=uR8o$D4T$3nPPHzYaQkb%%xNnMGNjZ&x=s8UKptn&kZU z%a^x@FH7Cpt=-_{b2e8voh|%l`;{kON=v@_GjW-24cm0?+d4k3ZrwL;UL?=s5y^Tv zciQvUk8@Jq%$gK!wXla*<4WMX7ta&do;|%Vd-l9QgH)@46)I{6PPzm{#Cf`$xT31J vT0%oaGGpmcJ+AE((Bi6 zF5kSf_vq0(4Qi-zuY<-Oy4bugtT6J1_t$GIS1}1$b zPOF^U*10*&cXC_o=D*25U~_=qTEBqx0d5oA?aS>W7exAX`em%m$XK3{w;@kiOIcD% z^8JVR2M--In_%kR;1)GKk}r!v&q&+R)h;GJGBG7CH6uwvT0~JrHaN^*Qd-10h||T3 zBO@cVQkOL=z*~%&VY9CMG9l(HbE8NWhI1Jy`wH_IpfQlHCyxm2Gz%=c-GoFEL^hY#)oyB!Rdx)W?JSJ)$4m`=66r_{=qhKXLMwtee6=NHE}ugqcLp)(x$ro6h$ z#@gA%!pgO>3j#`3bgkO8Y~ISHy{+vV7p^lid(Xh|eVY}&z?D;$gp3L;h-|}VO7>68tf-^ zdCwRMT(Xe3Vk3LQQSrW)_9H+2H!*hK-oD;1$9!Cq>x2f+IYW`dhGM5uf?igqe`zoN z)>rd=V#AHr@`F;02P7ExOE4ZWlDTc8cH36%sh8=CK&y9=PRDZ-zD{g9UX^#Yq4Y#! z`GW=1er;QQbEzvImEMYs3w-Y?3qUyAjdhRCOAug`J5C#Uvb-nISHt5-L! zT>0_+`@etx{{H>@=g*(tzkmPw_3P))pFe&8g};CM_U+TBPai*ieE06%n>TM>zkdDd z)vK2;Up{~S{OQxDPo6w^`0(MaTeoi9xN+^;wJTSyT)K4W!i5Xx&!0bc?%Y|R|IeH` zb?VgdhRd z2wft&3aE%N$=lt9p@UV{1IXFt>EaktaVt6D08>}jr<)Rg7e5LP5=v5Hwz=!*#2Q(5 z@8P*+$F6M*biCJN=dke59E1J@ZdYC3%C7?PZtE2m1jNY%FX-K-vwuTV-_+gi4CVnH ztQiwJ=Qd1O(K2u1?s^xSfD>nDcVE2_7T^+d%Hgc~6iZQ!CxSAQpDa;SSJQl(At`e9 zIG+gD6Hgz{z)jb$mIstR^z%8-#?=`6Bx~84Bt2ol`O{`{i#+o2^K9CoF~!`pS($%! ZIm3t9>d&p({)4>3;OXk;vd$@?2>=XvP167X literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ke.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ke.png new file mode 100644 index 0000000000000000000000000000000000000000..7bc38588be8e29bb7f0546bee5df4d59ff1600ea GIT binary patch literal 1039 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFpDZT6OW)vt zso6nWyDe7MyF9&~*Ve6wjXSPycEZyBvVZ7azn~MLQCH)WU*?y5uc-Z4UbENG;(2cI zkG39WCT4p(`--}{+QvpJYwH!aw&kvlL^*O|iqmWm$yRf>?td$H4U|@f-3|XXWQlEt_|8nPP)M`mBP&1(j`^dMoDF zH?HhhaB4Y2jsR=Es6@58VvdeRnXz8Il}U%A#U$6*)tUV}rm_|YInD5$K7HEk#)K99 z`Lojkj!dZ!^z&WJz+lI~u$Yx~D<`K3GxIb-!95ZZdlVG5tEnweQaWR5y4%ojtARnZ zgv4nJi@hc$`z$S*{?7vf5)>8h`}sWz2sq>6@gO{W zkC)eOFRx%l#hX!)Pvhct`};phO?{n}btNTbfsfDCjEuMC<+pNkuNM_X>g#`PX?fMy z_@$%cY)Q$>mX;;4vEOIRkd}~eu(b^h3d%@LEiNpaHg)QvMT@3PnbOnK)7910+1XiM zUY?bem71EGn3xzB7Z(r^;P3D6=H}+)Y$8e8Cpvh(eC6aj&DZPc*W1s;_4N5M)}NKXjn$_dS6iyOsfD@K#kFF` z&X$>%if^>Cu>Sa2(dZ<$Ozqe&Be|tA^DeddJxgPAP-M^6_D)xpH7~by7q7OCmR>Mn s(VjJPnwE7mdKI;Vst0HbUq8vppj<4M!g za~MzcGhdp{^?9$=nQq3@eXQsE+0Rbqy*XFp&H{;V`|VD3GMsE>c)CRGbR*-HUfxG@ z6<@E?KUvLiwq5A%6xkPxw102&`m@vbR3-DpX0E5R)NVA3Tuc@?6~THjUj1v6+m8Ue#Gw)YsKCH@mSdI07D(gWF&QrR)E%IDv3)x7$s3 zhl|cZPlJ^XT8F(2uZLKi_BTE0XL8iX_)U!6X@9f#2~HP6tTuZXeN1t?6YX#<((ZPQ z)4o9KFPXj%65Q@5xIIbne3azzCENdNPQZ(FpVygwpK=4=X9s-D4SbOu@H{i%bgbLk zg77zm;UCK59u!1=sZIG@lk&bc^<8c1x2CKE@(las7!E43p3>qyq{VwiPvE$b#7Rqq z`|jG;11+uxS-g*Rx)bI2Io0b)lINp%kFOd2FH(IzX9T=X^Lw8e@U=eUU0MJz5`j@U z3P=rsoFfyvfqEE|yxm7D!N+crIw-cEcy)`@>yD1hCaJj}XR2B{DE|MHnR@Ke8m1VVqN-X~ z)=Ym(C71J$O!yvZr(uy9t)`Y>iivp7$? z%)kGZtSDFzkhx%T=;B#>6n0N}wq(nW4GaqcwrpNI(|7j5zHKL`e7SK!A%DSy-E&ti p-Mc#f{E?NBhJuQA^KR^6X803j5M#Jtq7o=9JYD@<);T3K0RX|@LUsTE literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/kh.png b/lam/templates/3rdParty/pla/htdocs/images/countries/kh.png new file mode 100644 index 0000000000000000000000000000000000000000..9c94193159614b98f5bcc94cef4e7314a7c5f485 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKP)8H!Y6P* zRQ!mz^d)Keb6UFh4NW#^>s~T7-)&^FO3&b}i`&<;ug5fN}SI{1tr z*K{AZ?V`eS>}=QYaL;7tNaW#}#V;^dP-w2CYEs~0D(#m~GIy3dH&zL$~&@x_PV71=D`LLP&4kzE6Zb5gfoY%Pp zobivm9}xW@AnI0l;;pRWCs+2Y|MBG3*)?-tUq1Q&)x&!Sx1E{Ub8ha$hg(-)TeJAx z!5zOYojTK0a${=O@3o6QZC-WO!{TC?+nKcJ3k~Jx^%Y;ZS)6gPe3BYd7#eWJ+wD(W#J8C6UrBL)GgAKNWcWP8tH$vvwCQ4@h>RgXF$L;KR;mP0;6~o;0ghYz!N4wImRS!cNc~ZR#^`q=b)#H zV@SoVJ2rovl^dO+*rt_j3v+ul%c`nPP3)SPgJPOoq99}0 zvX{mxH!LcCvixjx3f()oDoxby?Ah62CcBre-pr|S#cFB2pKDs7W0XOu_M~*)8 zE-GefJ@d76Yv@x^k*Oh5u9}?j>1}k1)BD!9POeT-F!15QM#g=1H})_yEYC91pUEtC P9OP?HS3j3^P6Mb>-NZWeZl#n6YBsk|V3u9STqEnzML+NYbQ){KbvEyX#u#cTZfgZO^5E z*!IHeIa3xKZRlEAR5_t*(%Shejx=`6Teh>wyDw^iZT6T2BrZaV|({~@eJ7fNa{JI5|?aQ-D`-82mKeIh@Y|_2^{g*EB-n}EcX&J-r0ejvZMy!W584fG4zRd_fCdztHlIygZ#0@u#JN}M$4Ap+-Wu3}Q zt7TxQVPH7U$J@!pbwNbnxQNgVNr`h35)%Xk@5sqMP*gmxpfF2P@~XP}Gi~jc`ua;0 z6>sY6pVHNNYijzz%4(gK)?EvW^Cl*r?d-lfIqfhq`r+pG-OcTez1?GHr-x2XKfSyT zTU-C~^?mB*_R7=qg@?y4fB)YB0lxwQ-ud`^^z-`>82CLf@M}Q8+kk)zZf+mL!!P;y zeT|QM8X5U5De*^2%IDP7-&t9|YODYCbp4nz2^hb?SRMsfLqPY~oGCyF#w2fd7lsa2 zSq~uRfTxRNNX4z>gab^ME_L4uP7)S=v?zHKmzH$3qf;esahi(jkKaGso=v~@-G_}U zHSGMHyRmZw#bo9O2VFN@$*mD$Hnq~LvaF&ZKA`5ZWpJN5-Szp@3$2;lp57Tuts%Z96;D*AF5S_h%X_8AH*`ZKJHx4U VM%z9=T=E#?V^3E4K z-W#2^JGcBet9UDiWE-!1hoDlQqR|{>ld0;)^`@??^{qRtJ?5M~ckb!)XJ=+kdA)se zw-C>3bG@CqYUizuAGq253-7V>ST4w)L@f=)>%`-OO_^A zt;~LTIQ{T+yk%$gz{&29kK0psr&r!??|eP~hx-2u3-}b^^EWc^Td?1|V4v6i-oHZw ze})FU%uM;#-SKa7bn)ZIkLS*v14b?|ibnyi5cvAG^CnP^G0EHAg`tC0)&s~n z=;`7ZQgJIe;Q&)e$QGWnXG=`4ObNYeTFT?o;AG^Lq$G4`(WanFMW47Hbxm?m+&<-0 z!u16&CS3U7@!d!Mf6#(}==XOjFF!xevAR3s-HTV6r`Wg*L$CYg&W*WzUtsEl;D|}H z*+gcAJhfT6?|i0Ha*Ug!Yh0C4Kt|HdH#0XcJDZvH@{Qj4^Io~^TtAN-dvof->rNL=inAo&yX5W4tTjthR#aDLg#vW#d{M|<3 TztlCZgM97j>gTe~DWM4fHNs_W literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/kr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/kr.png new file mode 100644 index 0000000000000000000000000000000000000000..0df561a99300dac225fc795d0d099c72ce9fe77f GIT binary patch literal 1040 zcmeH^-A_^h7{-sG)3J1#ZkZcf&0K{m>!z(W3=s!nQ_PQbQ&Z_C$+cn2Vm2KE12GK& z3rUBLlg*X#bs@<@S$+cwoiiLw5Ytp71qK*;lurLdSH0WwKKot0@193MFQp`7v#SPiI-FMBnP5{6F^xKt1D8^#3)v{_Z^;&;<{o8Z2`xs(>*Gt)EN zs$Lqo=PXVxd*i@jyD~F-b93M4<*D=Y)fDR3O~S|5-OFu!grq^9{#<*Z(dk9K0ZD=IzkZS z;NTz{jYcAoP$(1(1_ObB-|yeu-Szo=Uaxm!V`F`N-Q)4N-ENo5wX(9}bUGmjvfJ%8 zn{8=nX>oDUYPDL>C(P#g`T5z|S)yyjnGMP*&l}aQMu~;k;iG)I-Kp^1pcw8=*!{M~HwzjmiG&eW1+3cpK zCKij;(9pnSG8qg8jYcahEF=&JXbWf;|K}ek@Fam^LQlrkl~Bk4KLJNY;Y%^Opz@iM zQ8RG@kJm_7?jfYXL6fP%fcsTIik^u+ubmW?r`5t?PluH_FbKU{Bi+c(E!hF!VBO_1 z1)Z&WJfeH0SIc+9>;D$+N9oK9V zp)hk{d}K#a_M+0EAF=~>Bb8d)VosWA_Oju`X{PY0tazD?K)c9#x$+Pn_7KH~j6l5-p z32)2Gn3odQUs1R!FJpFT{?d%(NfkxQbJC_&m#!?%om-GStFm}$N#4BL@>Mlu%e&h) zcQmi>YF%$;kY}o&W1^R3ZkX%gRF@Px&ELB+HK`#jrPA8G*xJ0v-MONveqC3`awmt1 z;-aqR<{9y6(`GJQuxS1AooBZ!*fnw8(b?1Hx1Cs2vvbyjSIxQ4yUIT|WUq?!-j^Bu zvbpG8NBP9Wh;Q51JYKb6yR-2DZ;J*))${Sb*9#JNw$>JTxE}6k*;rTzj7MN>jslWG z;5@U_2cQPVByV>Yh7ML)4;a6qP(T z%_g8DG9srdLDaf^YVpDh@9Z3v0FBj7)8eD_-GdHVEA3twrzN8?W#*g6Y3liL4@5?HVG+5+yTS01`PQ!-z8=$8>xF$}=W?~4@q#&4L}>cFSsIVU*YJoGZM|T=rR2-S k3)egY!+AfI9)2dv;Qmto6*nv2b5KBdy85}Sb4q9e00cWbkN^Mx literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/kz.png b/lam/templates/3rdParty/pla/htdocs/images/countries/kz.png new file mode 100644 index 0000000000000000000000000000000000000000..eb7b9bef2b99b95be3d669c6023dbb4346b97e36 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFXJ|Xa(0+!o z;|x>BY39yz%w6Z0JI=6nUtsMz!`^+7v+FGH zZT}y~_0R2B-nUU+4ssj}B`UtHQF8=ss#hltl3UQY8lJ8#mPq?OzvTCcbSy=TmbyB#;xO7Y$; z2VsqnTS6y}&k7BmKRYBaBYn|QCXrR|cpfA?JC~V}_qx;gdT=-cm#Fzq<3g?ujiV{Y z*j{I8oMLJ{^*iE5W$V*k*}FMBrlF}S8!FiuR=4Rn1~>bgfx^Pm)z4*}Q$iB}7Y%K= literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/lb.png b/lam/templates/3rdParty/pla/htdocs/images/countries/lb.png new file mode 100644 index 0000000000000000000000000000000000000000..44f9e1bfe35f515ed50884a0b725bd16b14ccad3 GIT binary patch literal 1042 zcmeH^+e=dc9LG=2VO|Eq3N#zJrjk?9C~w3l$DB@=F40_SVTGV#P@rj2iGf|MK<%ZA z*m@Y13murZl{(GKJUd4Zn=UW4*_p06kDb-7Zaba+gx>qX_xI)d@O}Aw?~C%XXhHjf z005u~vUvrhhms1}O(DM+WIP4{3Lp{|W|EO02=E|NeVE;IIM$lU_nZ;?N=oOIW zU;lxkPKRT4WyR<7dA(kb$FsAu<954UE|=YIx7ln~s};v_48trIi`i^8nM_8b(O@tv zEiLJEI*mqyAP5Y@5Co}7d?2V&siaaVIU+gb|NB=0EN4kK+1Yh5H;V_{qcXC|Fe>0> z6gDhfDj)T0ZGFLN`|ha`(oV%Q6kJI&*ZwK6>dpNOP?(3^I*kirPcl2@X6xI6^L3Xo zW!swMK~C4)@4y>deeDzb_VI>FH_gk(nz??h2_N@FS3K=77VS5eG{*R~R6jfBFP+YQ z^L9pXG^HNTrvz#1?fIQ-_N&)%F)O-xX|(@d_B14f^?GD+b`6AQk4#g8T86~WOTP^E z%6q3i-=PNd04)d9A&(MjIDV{4O7Bet=Ungg0ASUf=zu?MoBqNOWajbSbIWi40lScu Apa1{> literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/lc.png b/lam/templates/3rdParty/pla/htdocs/images/countries/lc.png new file mode 100644 index 0000000000000000000000000000000000000000..6b0389ae921c2e79846e8acd31ab30ff7fb3d215 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF5B3kNDep;& ztqBf{4GYPfG-0`=X@#|UxwCz}gH5H6M_Y2-{P2)Yf1j#^m`TwQ9YuL7(o<&SX3fve zn3J6`D=EG=KWBbYd{0T?vf_fp$q7C6)tl<7H`G?Ht14ezTeYUSVRK*i?(EFoiG6#g zOgu1o!v2`3vhvayM*3ySik>P;UiLP1wpLXRwpDJLWsXJlu-oV`oH1Oz`q3b#uu}PMnjO zwkSDaMqJFKkf5djznbvS*07M~_}KoKsIJ8LiKRtr(^F@b7OlxlpIuqDAuVNUL|8*& ze0O8rj+)91WhE=>YqvDiZYeEZ)>yZ-w0LQK?WVe#jSY31n;JGYHEif;+ul&Oy02$X zU(e3o?j4gS?oUZBGd3)Ca%}eVEzizc92?VaKLr6r}M1d|gZo@`Tmu|sX^M1`-%tzI2+`*$T^-$KWS8)WBJ zimYsrc(+S`-wf?vC!9{N@{$nc^>nkFS0?ajx9Oh~ZvW2t|2!3NVws8*Ln79;jfapOq`#l=lLPhFyC{$eY1PFK9SL}I~& z=#8ruiStM2#_HuKmUJ~q1ZV`=hTfR6KIz^a%S^|@PeT737OpAb%#Q7y)oHx?=He$y zm{}X0W`6AZH7#^=)i1TsRi3Tv70j((EIt-q(z~N?shLde6#My+xwYx6#pju7=KeEp z877ylSl=%kaH1``d+FBI+v{(A`R13#9&Vj;kC9>a0>j<6o_?$VdEC?0&t;ucLK6Uu CQan5W literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/lk.png b/lam/templates/3rdParty/pla/htdocs/images/countries/lk.png new file mode 100644 index 0000000000000000000000000000000000000000..2f2c48bc7dc9166b06424a7bc52183eeec7395d1 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFXLDwd2ouz7 zk{2!J%-fzK62PTeC!w9CXw#=}QmW-L#lp4CI&`5&%!+`(DQ=mY;|jK>mhH|G4`2}Y zXE^iF^vD&pPe1Iw{BrvJ+xP!}|7XuUfBpUTpB%Gr{54!MHPv!;j31yuLo;S|{(lg;vuGjdnHZ9WE1i zFxPEcGQ;*%#_j2>yEEB$=LsFDRzKEa{dkVw^96wit4vme^FHafn-(JdVrsy%sR3sq zm^TNoo{Q(blB4jj!Q$hTfHPqX=OUQ5`Lq9BlJb2)>PA<_CoNvj+5*h)IS%czfzm=dY0c(Nrq*ztQ*t?*62%YGM7K&V|pXj zZG{x;9uvj&vbP*Yo=qqD-mV7-aS zB^Rd)PELF5?Jl{w9d&a%>FIgH-~XDw{}~@2U=#x*dK6%Wfa;sYh7ML) z4+>D&TVc>|MJ>~npPT%r!cnO z*!lKxZ>`!h^{i8oEK9eZVdg62vT%{E`o8ptW~a4k>Xq|sTpzh^yGmA+wJ%@QxwG|G z=@|wtPR?5{uXcZDo2BjHb^27eKRegPkCARKs(em_O`C9OLSW83Hjziqjkqs=oHb=~ z=G*-jyjJIF1$49@HgP*&T6BBPlcsYF(~pHM5a3?zxn{A~yEQ*!i*L`)TF#REnUBGD WqtVADQ|vWC{`GYAb6Mw<&;$U#XhtCb literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/lt.png b/lam/templates/3rdParty/pla/htdocs/images/countries/lt.png new file mode 100644 index 0000000000000000000000000000000000000000..c6252cea6650cdd9568da3a6183bcca6dcfbb315 GIT binary patch literal 646 zcmbu5>q}Ds7{;ID+2+~hY&ut&VXdO5bSj9~K8U60K*h|!g3!#x0#{~7^iyO?VC2-6 zScFtW8i6-72_w*`=u;mmLwBDEDS4?Cshiu|@o(ti$Md|rA6{Obw$hRUUJ<7Nz#9wm z%Ea8ZHFK=!SBj>^1#`W$s65~0gvSlH8!i{@PWZfV*zw^#0vlW~Ag8c89MTYN4+XVL z3k;ExLJ|r_5XmVZ`?Y{sikc<$3~6SFw-fK7XeX(C6yu_(k89Ef5@7|B2o!OZWCE${ zNYg;NdWxy>bNg1YcLn-Y>{-UnMZ_;-*AHmikZmF|1a1>LLi8qL2-+|r{Yaftrq0RJ z=J~XFW%@T|rdOKoM7Bqo;gK1>pr=#S)2ZyXD!Q$5n^k4&&~|m`tX5syEp1CH{~`%v z280Z$LZ$1`3PygYnrc$9F+5s<1jWU7EvvBt9N5)sK?R{E2XEHYSN zFb8-Ucp_kD@L8<#zZGuowTnmY8S`?>o9t(&PHNd)9VT};#x0Il8Z@+uNLGPjift6h&F((cxa?!Y()VREZY5T4 zES++>WA*2jWv^@J-S1larFGfstkyk6eaAajy=$0rZSv+X{cB!M-}+_Zx)*29Up{{F z?9bo-K7ILq^wYm8XBcq3wmcN{wb{iROGB*0-?*81- z@rS49F>CAp{{H_01Ahht{Evuu6BP6=IQUz5`0tpQUvY8YQ&azEWxY&F`kR}3cHh2> zhYz3Gx9|4Zv*!;TdU5O4-3u4~ef;?K#ft}5uKfM@@zb+sS5BS!|K-caXU~3o|Niga zzdwKe{QC9l=g*(tzJ2@j>C?M+?_Rxn_4Mh}Cr_R{eE9IjjT_glUAuJY(uE5b&Ye4V z_Uzd+XU?1gn(+TWFe*m@sUaZQod--O42((M?k)@+tg;?J&VEl9$B>F!$q5IT_UuWz z)cr}wsdn$orbRP?jU1g6JzH5@$>cuZ6CO_esCtrI-oAbgQ=)C`mGdFIg@ z-E$gD{I+@*EOkqt<(RNDuymPM)}GMH)&BXLL(4XXl&lXf-WE|g%PVbPZ2h*Vs%60i zCyFNQnzQWaicOcc?mxU{%j*j_-(9);{QUKgw;na|m^JfTv3s|-b+jNN9wTjyI z$+-5(x^~IBOjPjbR&blFu^A z$0=#4M`pKe2Vxuc$2RPau00UfxHqUeC6z!vu949 z`SC>lAo;(SRAYe3&0$PNCoWovupt+1m-tI089jvk*K+ZN#7srr_TgeFrm?9$Q z3VRnjz4hs}&|&?%=(3~JpFmYsL%$feH_w>jf;1%^6y-J^N~%oE6La>n3;$@5YPn#7 z`?jcQ{i3rQJGV~A$XT_>Bw&S`|Mp3qd-pgwPmYXUzRQbS<4VBwJJ&8=y?5(U#O=uY zSFZ7jXr1OS-mrC}IH&(<-BZD#UF=*dE21S{NPo6sJEfxT!uE4FbF0b&y;D=B@_Z|0 t=CUl5+%%gpcFL560)j#hUN)VzW{CNu747x;N);%WJYD@<);T3K0RWHSH=6(e literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ly.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ly.png new file mode 100644 index 0000000000000000000000000000000000000000..8741c4c76fe3c2370a9beaa901c133c98a7429f6 GIT binary patch literal 623 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcX*_=LCux&QzFXDEwL!m| z>0DpaIlcycZrXMBTGh6CO-{NE4qBzwRttly76cizx)`*$m~?v@ce>jx3A32$XV&ZG zv^>gclAlqXi`%L=$2np4(}SJngnO+`v}*UYZuj+FpW?P4+IL-w=aM*&MX~-HQvEli z`L9n4*pTkCJSlKPy5Fjl!1d_?YtsVOqz0@>3s{*Nup-rMN@V!D?6@sOahr=GS7fK` zs7T#fk+G{fZEHo=?pmXE*OVRQpunLeaNqw{2WUQHlDE4HLkFv@2aq$@)5S5Q;#P9P z0j7wEC9+A*XOHZdVbQ8$x@NC~)1$D!M6)29GjIL~d5OvWb5L~L+fiX|$rxs47g!lA zZXdp2LXk(whGhoq+S@yN_U)a&T{qx_VBUmXTc@v|YZ#!RCX+Xzx?%H9#Q+VV90C0c z3hMUZr!90B1WcG^z;1j&fw@{fKUz3ebb-JjSBK`35Ya{_gJv7%xo3N~h^B0;WM{bg WS!%{rcd4(SQ1Nv2b6Mw<&;$TXT)2?{ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ma.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ma.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d9234e3b0308311a709755fc6ab8a44484c980 GIT binary patch literal 1175 zcmb7C`CpWE5PrVj?|UpSys#{+94agdCXsmHg@(%Q60Af}c58xRrKXX1q?Tz#_i1Gp z-M3+#!4=Z7j!)P;!Up0KK@gJX7{8|1iN%BEc8F+s4n z)2<+CV1@rclFpRmTNNkOrD@CJRc-71y7fK-(Z1G5@8N`?fko4-0-S`{2^}Kn0VaV_ zpkFyhM(b?UE#*sNSaB@57=_cpC=NkKj9kBjHZ5ePVD$N6l*c3`6O{FXI0+^NOof@r*cQ=p#(yGXVP{#2b6GWAZ7c5*9v3SYSWy>R1ti;Vxt5&axj){$nPe@#=Pr4;J z#jq|Fx2D~eo^ksf>+ihl?tAXN@BR%NAJ}BbMAqg9v$s6-@FQEdZGUvf&c|{d-<7+2 z5BBbRV*i1IPd;_%>BC2!dG@(|S$TO%_xTrIeCg#^UVZKLH{N{f=-cnSd+fdUKll*G zKFa_2lTSbU{EIKY`udx1zx)39t{)2a;KYd^fBN~CUw=DUkpKIsKmPpdbnYfYA&SnN z{k!;|l5?f!3;r#;aPd-k`Q?gAR8`Naxl&uVr#`Zw(P(J8YA^;jx1hDH-DK|Q>{54I zdU~zuy_vR^ef?~cP WJ|#8saAe4JHuUj^xZ^P!4*drhPK;;( literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/mc.png b/lam/templates/3rdParty/pla/htdocs/images/countries/mc.png new file mode 100644 index 0000000000000000000000000000000000000000..8dd6d29069c525374decf238a9f6e74ca403baac GIT binary patch literal 1040 zcmeH^>q}Ds0LAZh40SnDo8hCSgHTg!AimJFwzj!zQ?JgjiHVet6br%+`Kd^dj3O<> zib_I;iugiBG>5NNlX>r6+>JeQI@{8=HE`hC=C<4Qf9PWe&N-Z~zw->PD5X*oDF6Ua zAwH*?=vcy05hNmSBJ`I4KmuSv4VRcWj>|3=df~#ygoK0CR7Yl}J}u3~WR5Zz+f1fAH`iTI za8y`0n~`BDDDak)_}OerQIRG;e>ppQ_ex2C$J=MILSMo3gqN4wxLg+mIr)6N zvN8n2Zh;_JRdrld<%8jnQ0Rf-Lm0+*JWp+{MRU z!>CxSo0zaHE@IQuE}2X|KJLe`eW?^T8hu)=eRI=qFnA4yP$(1(1_Ob>@$s?W@Avt9 zUa!~V@f;l;x!vx=!$X(L<#ak74u{=tKR7tB*=$y;)nc)j&1RFyWHcJ}dc97k!!S&v z(WupGl}e>lDisO^N`OWXxm+%j$)r;0>guXQB3W8mnwgmy9UUb;BA)uMe@?)h^7Mo( zc>8)O2k0f!`NUX(IINm3tLRJPtskT@ww&F z&{c-$$687&y$yT>aqkJkPp5tAA7ak+t$c{?86!PXl0ob_uJd1Ce!6DNcz}b3z=f;%v+epY6q<&Ben-Q z3lG5_xudQwaeT|I5q6e2jD^cj*iJO7Jl95k_d@iwev;l%te230a6wim_h*OUgcZcC K;7qX_yZ-{kOl?X4 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/md.png b/lam/templates/3rdParty/pla/htdocs/images/countries/md.png new file mode 100644 index 0000000000000000000000000000000000000000..9d9d27807a03b0334b2def2613917d0a04ec0193 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFziMj6AU?&y zdvie2(Uj853<6QiQq!2Frt&J!kT+SZXw={sw#~|Wfwj-P$n5K#H_-9nn35(aYwmGY#FCdr#GkTL3()GAlETWa98O3S9p z!f%6-%M{zdg&whc+#**8q#g>-IUZMZCM0cZR_*1?s-q>XXEf|4m(~ZBRvXTm&$V(L z|FIKXo40cBJs^1ctkkJ92DNo|XU;I3JI8$H4C~pmoQDqaUb-X^70LbNiQ<(j@;i4) zU%sOD?w$U_hZetnIsO0d|L9;q2 z@1F%;IK+DGB=7CB5*H4sy}IwVVx~wzhRCBUPOtC!UOMJAwbwH>iaE%GvoV{YIhVbo zOnpw5byc2YnkPd;xZ*M!){X80efqN{sQN9VYn-YElvvqpxOOia!j8~?Dg z`{m?x%gXA3z1<%-x2H}{|Gd0jxw-xK^?m2*`QP9FUqHYoAD_R0f#3Z6eg_8r3>l+(~X-`fABEd&V7@sd`kOO zwOVt_lPj$}e_~~G-YRjgwpKsOz~#AuYs1S(Yc}?o?xLkWGwcL4jx1I^v+CirIkUvX zWAB)kdYK1w#K*TEJ)xtmZf@Y})+tq$1L|wGtlhbeOJj+G q-YLcR?;bvVD;<34Qb2I$S!;%>U_*|H`F%;Cfbewnb6Mw<&;$S^Iy)}_ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/mg.png b/lam/templates/3rdParty/pla/htdocs/images/countries/mg.png new file mode 100644 index 0000000000000000000000000000000000000000..e93f20e0a095bb44add8d01e4c9c19c25a878f09 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&MLS-~Z#sj$gTY z_1N*_SFc^Ya`lROEtg4)i2po&w>BNWUQ_E{3CDg#-#Oa8y@v6t?ZReQg-*1}+2)(J z$}@JRQ~uUK^L%OVX==8miuM)C&b4YTbsFwXT9J!Py;^nSmRSY%8KwQ zvsc=3x56y}Ict21wg%>{_sd%9@6)d5-)#^&#WZ+=N!%Q}qV4`gTLbj+nC+@~eL7_0 zX6T2t8HBeP$Ml%xFL%nF?^dq$ulAG1gu#SqHe74~p{Mwzv5I@87SV zKYx7x{^s@TyLawfx_I&L-@m_p{rdj>`?qi3K7Rc8=FOXzFJC@<`0&P!8&|Gexpe8$ z`Sa(`ojZ5-?AbGC&YV7ddiU<#n>KA)zI^%IxpSvXnbO_eU0+`hj5=TpjsmhnVA|FB zO+d|zN#5=*3>~bp9zf21PZ!6Kid)GE2bh-m%sdel7$7J6Mpi^{_KaDKLbI-14~Pts zcqT0&H+jOOx$Gjd?%WRwkBt#~ATK34f6mPLtRk~6oVyYd9P&z7NOJm=d9x?8i6o`v z*+qZ5;L^8C#nss|uRi#D6Pgd6>uB|LP z8l8Ok=GxjGnlq^@D{5Ao7nhO4!fSo==J^@gRf;C(hBC)SYAz6{F83Fo%{P71#I{}g g=FQo{V)mYa;q4#8ew%4CtwDj}>FVdQ&MBb@0N0yLi~s-t literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/mn.png b/lam/templates/3rdParty/pla/htdocs/images/countries/mn.png new file mode 100644 index 0000000000000000000000000000000000000000..9ce7bc9043112f1151cb3016a53e846b4452eef9 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF_itPg(7ZCF zeQi+7%9#GGQ}^DE>Ry*VYhTukoeir`2J~zWo47q;)`8rmrz$sIZQXsZ_t2B3O&9wQ zKknXh$9B>|n~4XUrX6*ha>#x5Nw0Zl0_L3zTX8jR{hg%E_Y3yDtT_7Ke9954smH9Q z9kZHx#Cqyc>xuhqChm8fbH;J@X{VXT9i|?3nsUH<$z|^)m%`WI4O)6JeBGVE#b;A? zJx|&BEPMB}^euM_k9?>;^R4>C$A)v?>rQ=YI`idtr~2)s9+xLr9G~d+W@GBv7X2sl z1KzHP`>;Oc)258Q2@J){NEE_cCY$y%RRW^K$edO0Kf z?xe`QIo2m4j+uxaH2Ykp3c$*n;J<;oan*WD_ zh$q>hZwjN{R;GMy$*&M#Xp!RjT9)#mJaxXl;;V!JU=#x*dK6%WK!L}f*FaImByV>Y zh7ML)4oXRX^Yf49u35eA*zJjd z*DkP$%#xO{%9onHt!;)_e7b*bwrxO0 z8R9c7gP}m3xk!t-P?Nbpo3%uTwOEI>P=~WbkF!{xx7wJu%7iOWS)k6GzuZ`;%3P$< zLZZ$_Fj7mR)>gK|NwLRWzS&7S-Auj1Q>n;KG2cdiMu=jTwOYB8Mx~2Ef1v4{NbNcg zt!g*DCU4yaFRfA+gH}I-7C)2jAmh$Ji>VP7QzI>=M4I)6I4w^zs`GVQmFYM;(P>Vi z*V*(U_+75@*KZad4cPT0@fA;tjrHsk?%Go zDSTaN+?JZS%{3`I8dJA6X6$Os-qW77yN$t3g0)18tyr74)IhewLB7dBsm)n3$;fC% zh*7)0-I7GBNfC~766~kP+E0sio|9>l+6BDzt4zmrfO2v&GElWatWgVR!sqKqP^Vw=UhiMYm zv2W`f6dl)^$hYt6d)H}Nd*-5<-ZlqCSAP5NmriAgUHdf6jaz)V#exZ;4a*bLnd7f7 zc`}K!@7%Lp4hwnS-!QniYT31uU*ATuUUR$UpeS#BQ{dq<&Aqw7zq>yrFS^p;l&7U6 zbFkC$W~=DVl^?5Sc{wQ78s=?V@Zrm*M~}|1#PX(AE|_4VFKu0)k~Ct@S1vo7%1cg@~uCpUXO@geCxkpCoVq literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/mp.png b/lam/templates/3rdParty/pla/htdocs/images/countries/mp.png new file mode 100644 index 0000000000000000000000000000000000000000..9523df6e0170499ec84f630f6a4584bae54c2297 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-?we=mepIX zU$}AX@TrXG*7}O6nTb8Q>FvE8i+650=V)CQ?AM-}*r}mhp{x+*?ldJNU{Y?z*07-7 zqP*?x%?D@C*e@Knka$NM{wB+^iF^e-(Hzp@6PmEg>8q^aTH8VeZYfk3I z^pur_x!VhJHy7q@iI1IJTeYvIa#u;wx{9*RmF1foYInCb9c--Iotr(Wt7B(x_wJIS z=`*GsShnP(vSOv0O0AkorLK0ZjnxEet5RE=3SaN;@X$FCVRHif`@=(L#6&Id@oq^> zSP>gDFC%SrcINuR{2hh)Tk~?(7Zz+yOqg9(;%ePF*^D_~hQbhw=(mlvi%Z&0Cn1-0JAs85P^+>07*G=b;s= zcdlH$Y2wV))g9A|`&PEiSrm|&ckuX$>Fby5yS;zv`r3wxS*LDpUAeex*MCcWA&IR`vl978H@ zB_|wUTC&tMrfF~X534IlA}qWzu8vMxl8#qnHmCc|Ju10A_x(#&5g~)`-`~zWbnVtT z!^N)mb@in+mV`z`8Pqnp)fMdnT_wxU2|9{VBfy2 zZt;$`s=KRBp7q-n=CDwR|Lmr?Z=Zhl&b%Y6eL2Uc(dlPf*Q7n4m3Hku^1MxbdDIKW z)}Se=$%|`e%#4Usx~zNFN2tMRRnZ@#PhM7ufsAENp2s?0-Pps-a7DoInVi9&{UA?! My85}Sb4q9e0579g>;M1& literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ms.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ms.png new file mode 100644 index 0000000000000000000000000000000000000000..f92b84d512d29c18ca4e190413dead45da6d8cf4 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-;`ajZtC)Q zkLbDeGwvL{xnueENk!An?YP(&-kBDiwSD!Wy0q!1_dVRa`eaVr5(mRc5nl7Bbzh!6 z>0(L#L_4D?0j`UjESCj&uC=#pi3#58=FlA;us9-cNlfU<)VOVF@!N{CkJOZ&GtiuF zVX)9lZ>Fi<3{$=7ruwtY^ke&OKCLaizBGVrB7}+VYFF<>#wPPQ*se>}Rjo4hMIb5lkA z;ik?ry;II_J#&BB>YX#U9zON*+n(zW58QsV?aIyNhj%XDxw?Puq{Fw4_bscOv@EH2 zM*M^s2~($aZQ7pvy1n>CU-hKez>8@iUz&0c<;8#KEPvfry0fwT`=+Ijm&{ll8+b6l zsnJyTT1om&PrDLVr^EGiz?cQb?~bp9zf1PPZ!6Kid)GE z2bdyubgO?X{4_m%#Y?H%)on)^Tc3UNpKFu*%SO%6H~9F16BVfoF07HP6|Po4Ge>zv zRA6{?@Ddggq15!*hYp{Ko}n3d^UAG=WgH?&qGB_30}dQa%?l2_e+-Vm^(L}%Ej_v`Wo5Qx)NZyXUrS1sRWP?s`n$H|3a`%GX5~q{c7NJy)8NFl z?Uz)Q7+>SVPR^T_`|6Aw6xEe^bA_$HUyyjZLcmhqTr=k$Bg5`{hRuN`=Q=&TSx!^ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/mt.png b/lam/templates/3rdParty/pla/htdocs/images/countries/mt.png new file mode 100644 index 0000000000000000000000000000000000000000..7f35e8201b8640e8d95036895af592f1820dcd56 GIT binary patch literal 873 zcmeH@KT8BL5XA@9LJkl70}6s*p&)|jS5S7m=nTj?Tm5Ox=5V+;6iYSo^vl`pfoTZI1fSusSp4uL&lMF6awQ!DXc22Yt$Nz zL2J=D^d3V%jO!C73H&R8owt`!Jg}ChyZy`0a(t6qZ4~3zg{N%k@oRB*|9E}#_Id99 YG})d4_}&_xU91+(!#wMzqrKw#2W(=X7ytkO literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/mx.png b/lam/templates/3rdParty/pla/htdocs/images/countries/mx.png new file mode 100644 index 0000000000000000000000000000000000000000..5d481f0678645656e39e71a886a35062c5f42230 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFziMjsXWI1N zv*$i|_3GJ+7ssxhJACSxP?e#2hYLfoB7?gKbAcjju_|Y|26u@jZ>bhynpB;+e7lW&leJ{Bo^rFjVz!w&o%Oh75r0lFo+g_Wot08qub=K~tg8l6bd2$RM!py~r zR_*>N+p5yG*S!1op{S+o;HksAj_$d5`*P>(o+%5bUB7qZ^`|#m_HI3K;pDM%$G-pg z?w{tJS)EeXTAh#{(=)X$Dba5K?hPpc@>!9FiIMJM0WP(rIXA9eiZu{iw`}3zHOn@w zT+vfq^+Zu|xwP~KMa6ge`kR!McdDuF(a`v0YPw%b>xi!IK^>jrdU~e}49*%EUNSK` zZ*2U-&hD3!(=98j2ljS<+}xfzIsNnUdgbQ!-`Dq@r{{lv|9=4ipL~4&1_plf^ZOkb z_%k5jMq1j#f`ZpIHD9~Bu2xk&ZEycHY101%3vS-H@$cWizkmP!`Sa)3uU|iZ{`~gs z+ow;T-o1PG>eZ_!Po6w{`0(Dnd)KaAyK?2qrAwDCT)1%V+_|%7&z?DR1{i(7NE`*^ zg}|Mpt&f2^8I!!-T^Kr8Wj%nL1D-C9Ar-fh6Am!>R4zH&JGV;RZ?4r!EhEM4EE-3Y z*X68uZ;^QF%E`Z14I__Ebx=&KjOEsyesbYM;a^!s+}d3YPMf6IW_ii%t5X!r+4?he zsilKrsB*5WWO}~*e}}MVlRoV!SukN~U)PkDy}R3&i&rP7G6rh}c=-2v2b{QOrEub4 z*HR5@T}h24ddE*{CtMa0dvop5gvk>{*hPW_9vcf5zI~aK`)-B5e`h}%m*=PA*`2pa lw;FCMzrIxMm|Nm!J_cb0gBd*14}OD!#M9N!Wt~$(69A7VSGE8E literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/my.png b/lam/templates/3rdParty/pla/htdocs/images/countries/my.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa6f816e8c91234bf7e537e740ffddc0eeef878 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF|2lo<5m(Q{ zuAZlS1I`78U5k!8oSJzkJ^P4H(3866L&X(m<1>y|HC$i0E+uNFiF&srPo1THmy}>j zZrZ-m+yf%q^^vh>jVu>r6y6n*npxJq~S2l8bdd^-Uq4nL}cJ9uzSI$h! zPgu8mNp@M}mOcF*ej2gKmh+b8u3Q(qajVmtHy-Emvd@xzwJ z+l_VStICg8l)aeHd%LUS$)x_f{XOsI&i=Q0<caLxl~wirKIRvTkEZgvVYxO%VVOS)Yaarss2B4!n=<4cb)B<(o#31ru?2f@j_L_ z{`|a8{k?ng^IrFKovJ9m+E90+x$)PG=?^Xc7Qmwa8h z;sH?c)-8XxZ~L@v-Mf8zudZ41>-6bY2loHJbn)l8v%n|@M)WAa3;{Wxm8?Kf#w2fd z7lsa2Sq~uRpr?ytNX4z>gab@d*7lf~T)q0FGUoK_nq@4l46QtCRln-=hCa2BopWba zl{za!tIrt~o;PnpmRL^WUKRaI{WoLlm)s>Kxw;}LKlZ5m&9}_{#I?>bH<9B9#&bwE6*Sd>@@?0yg-m&p b_?eHP*2d`L-gC2rLH_l0^>bP0l+XkKY(9gV literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/mz.png b/lam/templates/3rdParty/pla/htdocs/images/countries/mz.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6e87b417d124d4deec8324b2bcade831036f7d GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFH#0TMD=tdU z&d4puoiTex)z0FottEP?>dJZiss)1T1;ScI;yNV~`jyglZQ6E?I_^Ct0n==Kdu$@- zxQ9-5NnGk5JIg0?Re0{Y=$zFtMH>?{=0_E+O{&!Q)ahz$IP_!j*1N=gb^S{kNInNV1mH+AZymX^i^ z3+B$BKWEQ2)};%TH?Gmzv)$m>LDMrQt!`X)egD$`>4UKCo4pS03;OgnXkL=++wol;v3E4}h6lYJ9UOZRv?lt{&D>NS5Fx|OX@5o-0SC5@u zKXJQu-s$#Lw|CD2p56C*^*CVvuDU&|IQPtC-QUhNrPS@_6x&@L9(QK?pQzIPurTA& zq_kKSmi2z(r(@->&Z*m-?s(ioqFPVnc3ITEw(>Ux0d4YJ#|(rvIcl9Wk*E`7xZIR`ym978H@B_|wU z()wa)y2bSB6w%5bQ(9b9QyZLeOU2%N5^CBLbLS7!p+`ZN92PE{R#nXx87eAhSZb-u z8Y{aXz`rmuo!PjXarHvO*(*C|3I?pWV6}b4*8aqEYb1l!zWUFx5!DFE&5OKz*Sh@M znU|B*zxXH3ViVEQ*IfJN(&nO)qHhJ|rNsq|T&CO1#c%xj$==rD?$*-E#?IJkmG$|p zP4c5jmp5-R4ho(m)Zi3aX%={KV`nC#<;;C{_u@X@*u%^)`@NBRSo^VqAisOM`njxg HN@xNAK*&TS literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/na.png b/lam/templates/3rdParty/pla/htdocs/images/countries/na.png new file mode 100644 index 0000000000000000000000000000000000000000..83d63cfcdf5a989e515629d55d65580f6882d719 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKf}RuQ9$IO zqRItrqkBdcpL_!U$EW-(seISa{I8>bWqH$~Df8CVb*-AXbaK^%-u8tC`i<6Bb!n-q z85rUi7@`>&ZCF`TI5|^!dDDc1G9)Al&0xeoD&9tgH=;EXgsk(+djMYU@U8>-v_KFOZNc_4J>?C!Q}VUm`4K=ENPi zw-?X37#s3_>HLqYmtL?lxfkep#?SpzR^r3Hj&oeBw`Il8$V*((Q-5b=ay~NT_LPZd z7#PknGM-^&J;T9zmXq@e7uPjj-Wvh}PZSleD9FE1Q+utg{ng6qhn?LoC#OGdZvXxL z{{;k`iHNwFk#V@E8yK^|_#Fj!LLmO5n+H&uG0EHAg`tC07Q{K|>EaktaVt6D0MilQ zuc2E)o~Ej}sIIA$PCv)S6*+BMwtK=$0+%-y-D(_6@3-E{tcRqNbXL;Tum2b0Rw$Bh)Frj1Dq+SQ-&n#l= zoUZq@Y*1J*VcEpDd#=5!+osii$H;KL)`&fC<>}`jk9)fMxvXZV`K?q;q2iPo+78b(9m>)i^sme(8B?t#~6wkm!*8cqJMoWZPur z+vTORq!mjQl}eN~>NWJI=xfz!>2>H?%&{<;XkyTA;Iz`oZMB=zA}6=SZUNf@{5Sji zZS)J+5MVaJRH=zIVY6xOG_TURfrZloT+`Kj3v@#&j1yb!@~8SJ8Vba>>sKzbQZQuA zoyyR@TygC_p&R$r&Rk%8Riown740dwzXw6H)q_r zlPe^I_1rn${Reo{(m3zklem0YVaXEd8#mNTN+i6!h3?<8Xl+%0@gktQTL16gl&xDm z{{PEZupn;VzLI_Ws!L1bmn@l^7{_q?nuxm#`_(N1clXPFza01XYSxQY+9z7LUQG8m z)9Cp~UGcT4{(Do?-8woa3=A%sm>f4Ud~av>-O1^)wbd^-x5rLS$E>ZNySe@F^Zn!R z|H8}jPe8yIKfiB4GAQtSO3JsiRA3YXBYG5IhQP%~YUw~x#w2fd7lsa2Sq~uRpr?yt zNX4z>gab?}OP@?Ny&~$GsXy~6Dz;w^ii!_sb)CBO zhDk6_F3zsbk#T`Qc6exHWnokjyD_&kySTb_Kt@%xlV@O9P)>3A`zw~k%Y7NRbaQ&o zpZE3a&3-x4Ci@=GvbqHqn$8`X^QGs^o^PJU&(F^@+Q_f*L|$y}r3+Uigx|du7m^W? zU=Z14{v?&rdAUNk1nLBY>V!mEMT8rKCE6q;S|kKx z1!cQr=xTOt#PuNW@Xf9 zUcGqn;`#IE4CTTe?*@+o~z;TVirC x!r7HkL*?ArL-Xtws_$R0dHcbD*f^=#?25PFDZ` literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/nl.png b/lam/templates/3rdParty/pla/htdocs/images/countries/nl.png new file mode 100644 index 0000000000000000000000000000000000000000..99b5e139e4c930df1497829b3999920c8bc3a454 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF@8;vbdgbcz zW5-XNJbUc;>2KeEKYjM<#?8AYPM+Dj@8FN0e_ptsxD&V|L!hN%1&|$^E?J^D%lmj-X25!&{-Kyd>+c5dOhVNq2luOz{ z%k)Cm8bxn4OWbb~v%@HAqeb!&my$bfCHI`NPr4VK52$+-Q1j5Q;$~p&UHkYYzQsq} zGuB5o-SR8g8C82SZ^}~^t2J^#2h0<9xfPztnD8W{_jY#wt+VGZA3u5a=kI@?zI?xO z_3HEI&mNpOeujaeo`K;UGxG&j)-yoF!Eu(8^BOPj4FQ2Gf`aEnL@r54Oc4}(C@XtU zUjB)q;uU%MxssCa^!0D(>b^EI`ebVQ(86MqzTPhv!-n;kw z=FKlopT2wO(DKQXzuvt0@A2cmj~{=!dGq7Vn^*Sl|Nr8}kMH0A{rmUl&!1nve*OIU z^V_#?pFVwh_wL=RSFfHvefs3dlZOu<-nen&+O=z!E?v5C;ljCd=gyuzd*;lUQ>RV= zqYoH~qky~+Q17mt1k}lx&=gJK-&Iyovu^KJXK0-jbvH)cf993@w{M=haqXy$sK$}Q>IY6e zdvfCB!$)V%oj!k*SER@waPs7-;oD6&Z{D)qz}%FLE7P-!tJA|n^R-)_+&rhFwdh*Pd>$kq$xzDJQ)IDw0vJHpWwKD}w3WZGaWF2bM zJ?b3eCxw?RnYv&TtA1w3jCJi(S4>;FYr*=%3b_tzpcAt~NWlqiz8b%$JEVFYF}A6 zbz4fy!i@H%`F*RKmL4gcv}WR_i>-_ImGmy1zWwT?jpt@;I=TGN?Nx_vZa#H?`nC&a zn`=KFJ9KW(?z1))XCgw+MTehBi2gcx!m*0de+%Yb>FW5tbkP|GhI$5uvy6=An3*rI zvYugOJq}0ueG%| zC@Q|w*WaY9yhlUhld0)`Ev@5vdOLNrzgk(HGBDVrrFGZL^o*&|Lko+mW~P@+On%te z9Wgfg<>a*6$mqPKV|duh$jBF&X`c%6E+!}bs;#}@u3Wlx?%pvjjU_7=E>qIiUcP2^dcu;t6|49~ z9z9Gdy8dYDoP%c$9XxyT+%YbZS6V$E&4eesJDL6N&5HF}=UKT#kGEFN;y(2y%zW#W zDbG@tF}6-R#JpFhW$G{K+gaY9HX8?RsAOk&%W24CFqwA&C?Gst{an^LB{Ts5U}9e} literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/np.png b/lam/templates/3rdParty/pla/htdocs/images/countries/np.png new file mode 100644 index 0000000000000000000000000000000000000000..254744a23b4524efd4a080f12b70b43892eb619f GIT binary patch literal 730 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcX*_=LCux&QzFKhDMd%F5=X zvB_~O>z95(7u5{^zkajH!1``n#u7&11q{NQ^c>m41JCr>&vWy+al%g(M{`}gL} z3kMILIdS6S$B*aFpFeZv3@{9VAxQ=}-|M#=s4cG~$S)XZ2>k%V0#+Mipv{a)-tI08 z9jvk*Ku({hi(^Q|t>lCQOgcYZrkWNDGN`0Hmp-eyrb*|C+_V{aStkzZE?B@Q{K@Ot ziAl!ATQ+P;6%T3R*m&%i%ZIMh-lsi0yH2jkddbwf=n!+vj0%yj6VG(@czbyp8a@hl z=4hRj{``r=4@*Ptm4{n;9tRs63)$HIX};;QMbU}xn%TN@-}3An6&vr(5wfbfv4@!< Xs6jW%Ir-59P`G-!`njxgN@xNAqWfLi literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/nr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/nr.png new file mode 100644 index 0000000000000000000000000000000000000000..7f452297903402732194a6feb0be6f9d33181a52 GIT binary patch literal 1038 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF=TPnAQt#u_ znJj5CP1<6foLQ%$WskUeoUZ2@Rm(avzvT`wdt74H*@w)v51JB`wLiSzgm=Qqu-pT2 zB?l5p_vSWT;MHsq&~6vh1)?@#y%rvsAQAmmQT@uCK55eq zS@RwRtA2TlE@j(Est(gMoMvge%#hVf({h@i?>R@?x!1&ZzJdD`1CJ?Y0ZTNj%B(_G zs+s56hAp@7pQYzeYvA1A7`58its0Hq$L;v2*xr|MYDham!pI=Y(YMv=8hK&)?&lv^=tCe?=)vBg->$(c^ zok~mGi;LXqY6I)*LVLOsr%uW5?#h@myL|PkmiD%c4eLAi?4GrFlKj#siaS?YtzTeu zVu#P_dG1$_#U0)nadLOuowJ#@&Xz8lslCQOev|lNnOQ%zfMh^vZZIvp-YZVRgSKe57n}a zxTM&&HEP~;Q1m#w%3fWpGl)z4*}Q$iB}zaTH3 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/nz.png b/lam/templates/3rdParty/pla/htdocs/images/countries/nz.png new file mode 100644 index 0000000000000000000000000000000000000000..841bb9732770e1e4ba4ec6969deddee9b4b39898 GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-;!IfucYHp z@3J|W?Y9qIkMoG0T{C@o-;(Xiwl#;h%`aFysdU6t%V>-*$e6FL_A{W~w?hY%QZRUA8ul8_SyW6gIHeV>n>oql;V_~wu!g!XA z`4T&;W$w;v+@1R}QjSN3Eziw3QC4)ZvgA@#>BZ8bGfnlEEKC=>Ixh|mT$G=8F*0HM z>yF|Vebrx@au4RlzwfMgym-d&wr=x(#5coWEuPIO|W0JSK3quF1tOt;Dz|+Msq~ca`!U3imtX@?|ek`#% z#p^4g$}F_G(aBid|K;2FvyU#x3sF2Jb3cGVWR}^+)w_!iU6JI{4UMzPT(#3du~KiF zr_&szYpFtyOuuUBNI59-S#Eu4w06&(sef$V-mKbW=&N! zE0T3<*Y=xe6f(;^ZJSHs-axw!VaIN9p9%6IGc-JFH3Hl8(^l9-_M4{8btv89Q?kyl zYJ0F_Ip^!gkB(Uw9^qg(!pV4ym-VC&*Evz%s|pg2jJ0AZKfyX0W?J;e7}fam9k zz%vXCr76dWMztEGOqRUfy%Oyf*{{&I<`$l8`tnDe+KN_KdXjB^j9~ zii%eh({TJKYxDu^y%HZcTb)?dHC?*jT<+vUAuPW%9Tr(E?u~A;oP}%XV0EJbLI>% z5`j@U3P=rsmIM|SpdQ8~Z+91l4pvzYAZNd)i(^Q|t>lCQOhrzgPjgHEQum*4m+BPn zpUxhjv2@8MlkKIKw!92h|MtDiu#CC2Yt<_4Nm)|avtPY7iJEo$R-@CJqdBRHcI(c~ zt2@K_@@8bF!@?&`A!6pz#o6rN8#-?6IFYa*AkJ;a^tC&e&h5^R@Tn0ARtV5AzIFSa zQ*`+K11FB0xe{?%Kx4{__Zdr<>M1W@v1r+ rs$}53H2LY2DMEsi1jL0!jy{uRV6fDGX%rIR0}2aIS3j3^P69bE?ex@|6efIkE#yw|`p1VD7$=bd94}Jaqi_0Wm#Gy*nuQ{b{)BN>EFW-K4 z;MlqMpT0eR^^Q?Lli4_j)ijUYynxptpWmiX$i76vsYKDYRo%Z)GoVpFYNA18r&IPE zkK8$ag|joeHy2LWI&ahII}e^R7-lgWXS15-a$4l^+Lj79RPfsrNVrzW`ZURV*Xo8g z8^=wsNbL8oSRPQmc;BI8Z{L4Bb>`gNd-wMpJiPDd$%BUuZQ8tf@{}oO&z`w;^Tvt& z`_^-_ZWHF+Atta+gnzxL;Ce}seR2{9m1U2rE1ogbK5n3W!BqdUmFX=PySpAvSKS=% z`?&3PvVRohd&ST5aj5^Juz&~Qf%ifK9)<^>4fK7I5OX&+@p2qsabLYH0f9}HW zU8|TF*03^f;^EpUBC<(B?1ZM;ZawX5wpKe#OpZF*J@ECq?(g$3B=GjKr6*Ue+_iM+ z>HYgp?b~_OE_ zm&51HojN%@A|`}Ee+qQ1plzxhrqu1M`ho7A*bH(bJrzB5l7=>TGdM*6=4yIO? zP|+nvkN#xYa!VpwDrSysgVVHAo2+gH{YgtY+q=?psjqN@(=)eSwmdKI;Vst0FY;L9smFU literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/pe.png b/lam/templates/3rdParty/pla/htdocs/images/countries/pe.png new file mode 100644 index 0000000000000000000000000000000000000000..41794a019c0181602055cb9a89efde71833b6f09 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKh4W`Nk;yr zk?{pnvm;j4pOR93rDyyoE&G#K^1rm|%$)gG*RDTv^3<(sH%^~C{qF6%KY#!I`u*qB z=`;21y(i9|yZ_|Lt9S1%T)A@Z;lr=rzu&xl`^2eJKYsqqoY{Kn!r9sl-Ej*t`%X-s zd}QYKw|n+H+7~cAVByV$_wL^-nvl10*V0vcmRz`TwtH2@_a9%&Tf!$!seSwQ&B6&8 zi@GWnXL&z+@_6Iq_9OFW9q@NpKY8K_28Lq{40Q|)wG0d=nVF9>GamzzEG)-aSx>RE zpWx&?$;*3@pTCof>y(htSqX^=f`She6)!2spI1YU^)FxFr*3YqJUw4{c>MDB z{}T}KD#MSu;B8F6?Zmne6(-h=L;8pe*gaQ z{rd~&&YeGZ?&OIRzkmP!_U#)mT7i)}3h;-3%5SF%pbEw$Z+91l4pvzYAm^Z`i(^Q| zt>lCQOgV4=aQ|NBd3KiW>FIBUXNK~LZ2EleP3C+pO$7y`%~lqB*T`u+5xjpf^r-f- z#cL1TpA{Ao&LqN>`z#}AW5AT_K^If?SFAn5%C+(9b`|5<8l6%vG7CblJXy}ry6bk) zpBZPa`s((E?v(C|6mE2yyR~Dfx&B|;Y{u1!{#8r Nd%F6$taD0e0s!cTaC`s& literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/pf.png b/lam/templates/3rdParty/pla/htdocs/images/countries/pf.png new file mode 100644 index 0000000000000000000000000000000000000000..cf744fae21b27c2bf023aa410ecd7c6c95443cd1 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKNc2#B0Bbb zX4d1yOJA+ouzl+E!&RNr(>u#NtJ~9OO)6j9RlhSOdf}9wla(cl7tOo8WXaNf2cEa| zT%5OPcWLvfZ3kX|`TAqg`t#p^{=Rwp&bf=1-hcV}?eE`XXU;r*{rbked#BEy|Mma> zkH3FE|M+qF#*Gta&Kx*$WZkAs-+%vp_3qt^H*cQ4e0lo(`E5IQzW?;;-N%oe-QDLe zUi|g@_s?Iye*FA-;o`;f7cSg>aR0{LJ0CxNymR-?)oWLe9Y6Nt=l8Q`Po6$~^5n@A zrw?p9v3tw;mV`rjUZ?hK-;?S0Y~7sw^Cmwyy60e^(dF{Q{nL7mIO^`7(D``&tW(X6 z$D10i^mQNZ>$$sV!NcWC|LouMdB?UB3=GE^7-|_9Y8V(!GBclMWjz5zoSYYVc`pkH zoRg5aEi3y#QSqXJ{47byCDPKb^!3*(E59{0-L0i{+`!|2rVyYe2xK@bDT#!{1q1C&R)nL`P3>ak-P3@v^GwYggB!#)cgk8NVh^{yJ&m z*V(g<)z^KVH}7mu_nNB89WBl0rcc{9efsZTzkdDv`R(i1uV20#IeZw%12R8;{P^zO zySH!OzIyfQ#fukDpFVx`=+VQ65AWW+d-?L^3l}b&J9iEkH2e1L14c10qDKK{2xKz< zo(~jdO!9VjVd!9$^#F1Xdb&7r=4MqQ{dj7j>O#aGJW+_wCwV zoijdHO-sM>p4D|wRQ8P(l{FRL&yeoQ8k(w`yI{hEohMB)%gyEM!}BA9H?K5vSlGGu zT5-xAn;X?NwoBD~XWGhZm?VDE;bv#l^ONIq?EE+L^P2{zTH|MW1tL!$KYJ>^ZGPVC zEdm-T8B0~x=jmvwDe0vrtX#g3Uxe#5M;(F?{`H^l_P*?i!GPJzf1=);T3K0RUUjs}ukL literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ph.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ph.png new file mode 100644 index 0000000000000000000000000000000000000000..24007ab8bd4d406430886344699d5b2c5a0d7a27 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKdY>I*3;|s zsv>^>$zE z<~%htaAlC+Vg`mRM#daQ#%xB$EN12$X68&*);w0$9Cr3XPR=|ot|DIEQeNId0f7o3 zp%M|1S_z3d35g0>*(OEBHhKA4HMJf|$tZ2@K6Uj*ef>$Mrqivg=2%!vu(O-z&i^e);T*Ws4_1KOgq%Vaoqk4c{JB zpV``ZW#6VV3=HR(nJ=)iUSwy#!o_usm-mK%z!^co+aeg4p#%j=b!+kao*cb=aA{r&$11bp)G`5PGc&Cl<5VBpVy zfHPrXryJ@&e*F0G;loRpE_HWz10xq0#iIaM2n29nP65g>CV9KNFm$lWdH^{GJzX3_ zDsCkw9AMhB=j)ogvA!lxRiA`x?L8yg=p^{hPA+enRhN*_si?NJZF&w1e?DZK$H%61 zDacFe*D|wnWey9UEP0V&ZyM>zS{j?ItzON&AfP6yDkNor{{Dsq6FO$>SYZ&Lp=hdV zXwuv~wRi69>HY29!Wtsd;=)G`O$m>=Xjyn{$-8TA3ogu=u;FIwPS@14e=jflR$_96 zq4n9h*;A(m2Si=BPOXZs4$f5#(3otgZM}cVs_ai^ICFKsUVVCF4>QC1y+%emnY)5P OzV>wWb6Mw<&;$VZKSPHA literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/pk.png b/lam/templates/3rdParty/pla/htdocs/images/countries/pk.png new file mode 100644 index 0000000000000000000000000000000000000000..ff220ee22c62f438b177051a0846c0eadecc1e94 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&MLS-~Z#sj(__2 z`O39xyAJLxf6kNI)n+NF+!^#9u@#OiUs| zQX*7B++SQaR#w7SLN-QLF-cKAMqV*dQQAXVJxN_LKv6$WUq4r0EnH1MLti6O!?f5` zD_YC6&{Q{8*C5$IFHX;*z``WM#2~@IEX&L=!O*F}$*JDSvC7e{$<49C(W%18t=i4M z%ipiV&%e{(v)SV&`7#_Wb#?yLa#W`2PLx-@m_p{rdLp+sBU|-@JMA z^5x5i4IMm?(VLyuLnjZ zFiuASks+{e)A~a|rHo15?k)@+tg;?J&VEl9$B>F!$q5IT($3C`n>PDt`od)kvQpj) zit!80GF+;qe#h^0ko)yxsuo&;8YabA{;6Gy<0dTFSDBGk;-nLh5oI6j+Prbfj*iYP z0`6`x!T}vKH*UTXHMjfhnS0ItO4f(uHLhH{7_?^XiNmK}ipfltk$k}{lJ)+bc2J<$ zx>Rvr@9B@%PG{vR-Rh?ka>?Xu@=87ZiQ*s1Sh>FX&6u-3G2`{o^RddB&tAV{5m`0c qWaohcrz}-8x9&7N5PXbrb~%G1gQ26+t>r18AnXINP;va_G%ux^(Fx(B2Cd&YwSj?%cVvpb$T^d-v{5n>H<9zI^W7 zxl^W0>F(~XudfG24KRX60qsJ-&9Y+!&~(NmZ+91l4pvzYAZMSai(^Q|t>lCQOjg#n zBP2GT&YCsrm6msL@MK4)w`U_?s$T8&eC^gV_Rw# z1Z0~Q#|702N9Vd0`W1QUX$N#@TInfj+1Tiq7%7>l>DdWthzPw&U#g+IIO*9dX$g@B zk9kEtm4^sCdLke&*?jBwf(-^27`ZA}b{A~^+r>mdKI;Vst09(*x AvH$=8 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/pm.png b/lam/templates/3rdParty/pla/htdocs/images/countries/pm.png new file mode 100644 index 0000000000000000000000000000000000000000..6f9bf9076d8039c6005227fd73f2df272271ca92 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFpIu!usjmLf zg-f?~?48v;LC==I#>%Qx92tcE@CI zh)!RmW;Ipada9P)6iu6cU5Ck*{%f5>m$^r+af?_LlC~u_e@A-dshqlV@ph&S z-{d*-=BMNpuG_nJ>a1B;Z``6|iu)(lHZ zonPP9p4r=1zG+j#x^+{gOmbYjFt)M5yR$uT`n0q~3rlxwZ(p(4cKu41##-|UJ)YaQ zH!-c9&#`l@=#^v2@9yiryk&Z5o8F22)<53{ynE>P^k%@LYyN%h z#utwHG*;^$-sSP~UgX`&q1~-!tCzSvx|w)zNAS}5o=fKWb+-B*+*5J>fa=sPh3D5z zx31Lv{@iZOLZkId%#QAIo78D?_E4~ezT}!(iSGK8LsKm_#;~r8FwIFWbxAr=hrAHoZY>9@3w8gCfeCfMyuiSi6+QMV~=j-7Ha-9y?FHHB0&emX{^OJOO9$?US;)q@+l?h$BjQxkP4}B!1E<>%R8w(wYTFb{$GfTyWuv=H;|vkM@w1Z(mEwzfN17 b@R^Tc`z51~7d@2XLH_l0^>bP0l+XkKz=d;p literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/pr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/pr.png new file mode 100644 index 0000000000000000000000000000000000000000..0671638e9f51e57e45e4bf5d6afa3fb9bc4b5153 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-ytG;%G7Lg zYTA4;^$q1s?L1oUg1RTR9SG3yoK-t5%b_gRzBbsZ)=ay4-po_lYEhy7Q!9(Nlof8M zELq>*b!__NV;;`^E)Jz_jfaa17FSm+eEQ^*i$ix*_=3LfLpQF!3JIFy>RJ~Yw@gYl z#6M`}^x4N&tUvtm``-r-?w?pCr`LCXXbHsh6)zOBfK1^ElofAd43KL zejO5UG&1a5e9Y&0v%f4}aJHlE`jkme7BBp`X~UIya~`Z*zP6?L?T&3b`g@Peoc`nJ z;fGr`{XTW#)8T`smMuNMdezNsTR&a8c<EaktaVt6D0FwpJUzvr9pXI~$`U_#HD9X9gW69Q9WW_7NN zOympja0m=(nr~~MV`aE|$Cd_5W(^fJC6(iAlXH`^uV1>Bb^i{Fh)`J2mdKI;Vst0GQT%_5c6? literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/pt.png b/lam/templates/3rdParty/pla/htdocs/images/countries/pt.png new file mode 100644 index 0000000000000000000000000000000000000000..8d32759e8c04d837012bcde1f68112d541ce23f9 GIT binary patch literal 1036 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzhG*1Dkb$m zY{sP3v#lzv8KM{%!WkGW85k@Wm}8h(V_7-lIe9aAxtzHqvLpn(1?3CmB|{_?V-&U0 zv@Dt}jPi}FE37^GJpyI~xV5;&Es2X;5EnT;GG%#6*7~f}rKwpfvxHNG3~Kb#mZfs0 zvZk+0$y=LYRj(esz@uVqxLGN4_B@Bs0w(_i!P%>V>zdT6>v+q`)e8$9Bf?}m+QSbo zl-x4QW>SXzxm?+6rRv9%#gE1aZ1H57YRA6Qll`!-&^$lQ`(fHAjCmJXtLzW5nVlB3 zYQelCB8(@b+4m}mJk(M=&cJYzk?{mG^J!Mr6RfPKI62R8ab4i$y(}PbO+@5|gv5CX zi952g4-^$I%gZm3mVTtB_EcMYjiTaPef?MZ`Y-kM*DEV;Q&Zcaq4Cbt^sTAs8&lKW zT3Y*bbPnt39njT1ZeZ}<%Ic(%;V}b)2No6=%}mdkn4C5?{$gkM+0O2}lhaoxr>j<0 zKiu5DySd%5x4Ug`_t44dr;8#GvJ0G8qettg! z1HT6behmot5)kk$FmQED^xBjZV0-~%Z4^)?1THPO&kZz)G0EHAg`tC0)&t1d<>}%W zQgJIe;Q*7x&X$`ZXSkG^YMGrDUs`>u!6~NcQJ0b0MaO;r6eoI$uC;YgtP8X()lC-n z-~Zslj!nnjr7f7yv8AnX>E!Km!y|IyYQx=m13a8OZ#UgseE*=C*5X}zH?Nb>SfaH$ z+oJL6-HTcq7j50%xspLcMSo@HlT(k<-o@OyaXn_{d{&W3L4nsUZr;9;O)?~U(qtwP zA>juP11^8}WR{*XSBhUm=uztPXU4u7`7bjyPM!7XJNQhN;d-AzI&6&1U*|Z3qoHk`T9_ zfuV(!wS$$ljh(%nle3ebzgs|HlCbb335f}ciu09~=c}qtmzS?lRIJs|n5d;SNkgMU zUA@WFbc3nsIz7E0uK85?eg~C z6dHQK$*C_eaFv(WjKIJ}ad8`S^G@gF94ab0ou7Z``}bd8zy3IX{?d^n#}6Gk`s~?@ zCr_STx^(5}(c`;z?KyhveR{G*RCFC zV5nhWIKj+(nw9kg5OH!|;N`t6AaD-oXFM0G4 zm-_l|O-*kY8Qrz8`0C`e!^r4|o7*Et$EU6?m+kEjTU-C~_x}|T@FgH%osG@su&__z z;a}t8ex;mTKLqK|eYzt5kW0JSK3quF1tOt;@-_yl0q~ca` z!U3jBm%5~X-&$Q{9Bf>~rIj_?(W%nZR<^V?a^=gLKc8Op)pbx@cI_YMUEWkx*QHax z=FZ(4>!3L8=H@h?S!d21GcA0!MKgH$a)wqX=9*^bKbD7;EC1Fo)iygdIB^zUG(34{ zjbP%N;zf^yCnq&J_2v1U(~Gl_srkcqZ{0h&c@B!z+>EKJyta;s($>-0+TO(rCiL~p z=vXmj&8&SJJ11^i)}<8S5f>EW6y)C4liX8F>@F^ZWual89HnnJ9Z#Jfi6yrHCLBHV~{~$i$hdS-X`xSXqEK?0Ol+Z1a-UFeoK9|_QF6bMq_v-@ zw4tfDrmd@xoWQBGx}~nZthCw5(Cgdc|Nj5S#>dpv)yBui)Ya7I=jPqr-1ztR{r&y= z`ug|x_xASo@$vEO?Ck03>EYqw*x1C8r zos^W6i;IhYe}Dh~{{R30qz^q!00001VoOIv0Eh)0NB{r;wMj%lRCwBBV88-uYpasu z!vr`Kr4>}QP2B@PLe=G2Qj+G((()>rhOYj>K%tt7yo@+;E;$)x4Sh%7AU~i`Wnn^2 zv@n~jlA5lycTk`QP$-cxJ3mHG#<)mIg-}6KdbGifKjP{ zVTp)og}6n9jBUM|Ym-Ifd?VjBo5)FSNpnN;mu5C^EA8EPb`!%HAlkxkW-G(l&5UO@ zF`nDTd~Q4QnJvs`HnU#b&VFze``OK$hgNf5*~4{ZFW0p_yyv#_9$L+NW52+$^?bLF zh}=6Wadx}-rCkyakINq2Ao=91;*)cVSN6-lIIni?fXeGj+V5`Yzq_e_ZkO(-o2C!X zTKsry_v?|v9sBk)U_|2#G6 zWjcy0^_15esckaWccdHMf zZdX0+u6oo#=UD&GaRINR{hmkqe2NeJ5Fhw1HsHFh;v;MQ16Bqn z>`dNx+IEaktaVt6D z08>S!t0j-{p}lK-Cl@cjTH?dN$@#iK`pD1QlF`ea_~yT!&m|%>A>_d&{!%mZVxP0~ zuUaqV*HBrv{K(4J$*C!i=Y<(me_EoqV8WIzncdU6Jr}Kxuex}r^X^#(#nehoGu~)! z_4gBcHdWfL^>t8ml{5?st!D3E(6Q!{<=ft`4vLYSj$)?f-y4>^m^10zv|U;YCTtUs zpS){%end!4*~c?&uecUW;IQZ4oR(0K;!~6Mu;i_@% literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ru.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ru.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7093c18881dad7c86953ff272a5bfb738a1c3e GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFU&AZ0MnGz# zh}eDZUyv=$gn+$UoS(L60nRU~@|767M>s$Aq*s|~V@e4Pu+~+%FYZWW@=`|iPsz0QjFu|m1i$(JRv$`E_({H-= zUvTO=YF)X;p<%mQ*P(#9cl>5t4Vr&9VB)Fpxz~I;cV(@4n7RCJ-nz#(AHI10{@acF zPhWrf!4S8cIbkJh+)~cum7Iyoc{4ZhBrTUM-zQtNO}==Ge)}2I&NBw(>&+YYI8DFl zH0hFK?+LewXZ#o3514x^c-G~JMR&vJT#j3MH*Mv;?FUaDK6Cll@#AM07)~%SoMdLc zz{+}tmGuNC=S5!L8v+8CBqZ+0%05w4yrdw%L|S@-qT(xk{q@Sqdo(mYnVRm_(mJlE zcih0>p@qdI6O)T(rr(^L{B5Bz=gyrwd-m*^GiP@1 z-aU8j+$mG0)YsPoqY@aQqk#Ajkb9>p3{=gS~e#=$6VtGf%kwK+5a3!?RTLGL z=jmIUoSPb1$QYoZudHfnynMs1g)7&s+OtJNes(#7D}!O_Im5EapiuC1^>bP0l+XkK D+FWGp literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/sa.png b/lam/templates/3rdParty/pla/htdocs/images/countries/sa.png new file mode 100644 index 0000000000000000000000000000000000000000..f7982cc0282ece208059d5c7b989f4cb34b934ae GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFZ(iHNP{P1a z%)sEuz~I5aSjfm+#>`y8%v!<9TE@y&!Nyj`#$L+KS;5Iw!Npz9&0EjQTg%H^#misI z&lSui&?F#KD*}@Y>9^|} zbQoxrYZIA&nlmQ4FUe^`~x=z z2CNV8TjdwHKG1EtTfmxtkc}b!O9Fi6`S>sP58oIbz9BqzTWtLH__%Fx@mt~}S48?v z_Dk8F62BonVM9Xdj?}DuSsA-Cq8CSp&j}Bi9+I&=qhx zUzMM~B0pnpM(c^zwi9i6i}G?8=60OuC|FX^xVy1zd0EGSj>@H#jcXehTv@Q@{F=?z zHycdUH|jJr>o;|r=U_j}&VGhn^!BK<9jUonbCw-m3XDErB#r{|LZF9(nGdLwG0EHA zg`tC0)&s~n;OXKRQgJIe;Q&*_&5*sHCcjPDF>~jUsXcd^8=Z1Ct*gwO_i9qpoowV^J!hzqg!o@4o&Mh;9w zrm4Aezh0EP=CJVO%fDHlnt~P;`3X*bR4c2~;B@n!l+v3wOqXW$Ei`o0+k4Dm;SM=Y zKfB`hEpsj{yVGg5?3aULv}B%`oGbJ88CNE$R2w_zE|_3sZ`?j%PG7M{h{OD;ozu9? b-ZL=VT%db;d(BsQP>^`K`njxgN@xNAZx08$EjKll>xasi&{3dOY)5fgb!fDVfWYHyQn6BwP!zz51RcOCQ z#s+nZSdX-oewmBIOSZa1_Z0OUODbO)l+<1``B>M23$+vXF5Pr)$I(YDIt^@kwcJMa zJcc!VCJjR7P15$A+CCHHjUp`~=c-z#yQD7jNLw0Jz9qR~Z+h#&%+~$6od;8@S1jCo zQ_jB1HoDg*t<`(I5j@6F2kUXH*ZsbYrmrB6t(zecInGK6P8+~t#K;a?4PsB z$E{D+Do5I(L@{oWsc*ARP?tgY1e3@~W@!t73cRwGx|MA4E!*l}x;3zTYhclOzvA_N z_U!`BWy(?W^xbRJJnJ;P8?^jebpzY=LV65?I}O76j3XzRMocitUh5P$-70^BTk>4H zyC2)v)4=PJ7ro?XTD$=!_7Os6DHVh-kh*;9sBAP)~623E}N%tW)H*Jy^Lq}vR*sN zd*h_QrllfRj>*5cq4wgA)vvcscQ4o++strgC-d1|oR<$uKe?cIbgR)>WhA zH0PDZ0Up;vZaFx$g|98jim!KFQLPrxaoi`!ea7uwYpbJjC)QRp$OoLb5zv2lPhMVl z_RiJOjuZCl2b}N-Ik9Yca-8qN;DS28szv+(9j7-H7F8F|TgTe~ HDWM4f7Su+F literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/sd.png b/lam/templates/3rdParty/pla/htdocs/images/countries/sd.png new file mode 100644 index 0000000000000000000000000000000000000000..32398df2f3519f1e7b78df488bbb44c1b2121316 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzh>uf=I}8~ zGt0{tFDof4fB*ICVVWN=_$ zwr1u`;$+og<;~>fOXlNp=Mu^llE{^i$dQmQQ;<)W=al7CuU1z~P}I!V)XLSe=&`Wq zu&}PR_L%AsFgL)h(=A|jK=`8YxaDyvYf>`SXJl>2YF*r_qM@>Ihn?X#1H%bs=F_aK zCxD2P^8zpLWdVV65)y|cB#uf--jS7kps09WUVg5mAf{Iy=!6d z)ye6Ho7*Et$HUgvzx@4w2LyZ#2>1{j{3$&AM_ke;hDK7M@v;>Fu%&u(A6`sdG|@87?F`}XbAr%$h6zkdGw`O~LQ@7=q1 z?b@|Vmo8nrc=5=QBfEC(Dl9BaPft%wObiGJ@b~xk^Ye3ab8~WXvbVRlv$HcbH8n9Y zF)%RD*Vot6)6>$@($LUQR8*9em6ev377!5N<>h5%Wo2e&2F4vQ21fzeAy6ZxD+bie znB?v5!qCAg>jC8K_jGX#skoJ#aDXXi4)fy6!iVUZxM6O4^K@yAl$FVP+FFSLfpI|*0U=SDTq2i3!v(^PlGZ*j*VNHly=omJSLO`Q zPKixhOs`#+x_;CA=2oWGkdz}5YW{Ds^Yb#YUcPwumYM71Nn@XxSMFcEbmKzK?VE?q b7-yF=NU$2nuG9J21_~5US3j3^P6uW49e>nl{Yf0Y~<3}#-&gp zVRb;w{fxHHIW5Pzc5!#yQXg3bZT8K6;-B+0Ap2HI&5P99#|+}xjH+u`)Yq|UZeZ0| z&!)MSU3&|M_6km&Wn4Pz_zbrRn(P!bS|Mh+N78DmkY>Gv`64-oLrN~k6dZOa*soWx zTOnoGq3(H7#$=+R)huP(IR-%&lq@G{I4?B~zoO-`T;FTGb<7PzpG~HLyDYj=rDMyL*G*BmwoUKQ za)XUCbkA=xJG;qx=OWu1dp++R_Mg>Zy?l!6yUQsjHiayh;IVFA__CSto0g=X+Fh|@ zRsMo0iN|-=oZ8!T?c^k2oC0HZ6c7vn_9B(PKt+s6-tI089jvk*K+b+o7srr_TgeFr zm_$U+E)`buGYSn2x@frVI~N^+`;a`(CVVH=;`BKR;Dbiu18f< zo&Gd9Z3{Fknz%N1)~hNpzkLrA?>Q`-S)ArS`@)S2_LW{=zs!m>UdGg_rS0`;^{ZFK zpSikDpYrbJYIO3H%-mzS_3X{gm64h+XJ(#sP?QTxTXtg^pJAC_o*ti_nb?8}tM^Im zPxRj29ulykzkBgq(SQ>!-kvAc>@^QCJ9Pi-&4aguW|uQO(l+>UwY=^f$n&1AelF{r G5}E*)olWlm literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/sg.png b/lam/templates/3rdParty/pla/htdocs/images/countries/sg.png new file mode 100644 index 0000000000000000000000000000000000000000..51dc5a83e797ce924d32b253f423aaa39dad0a54 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKX~ZSrp=qL zT)pz(!Tq~;?_9llhYte_U``q>c!#eqObF(oZGYO+L1#)o<4qf;oP%pSKi&b^Xt`%?uPoayLY|1 zee3&+=l}lw`}_CrpFe+o|Ni~!*RP*HfByLK2A{`~oK z=gyr42K|{cr%s(Ze*E~6BS-e{-@j+io}D{)ZrHG4`SRt!$O1;$D4B=`jj&O1he=E(n-WK4Jdqg?-!BO`N%P@8o8tfD^h$u3tOi z;D7%{%(aWIcW?4(Op%)}G4GYI^z;c+)`(6O6XX;*rL!_SH$QuY-r3|cdh4DhFmZ`) z+`47E`PC`nDJf!`wv~KkYF#CD`}XYDTHKqBK50u?SO02o(#d_h_G{=-%buO7Z)ESD d`FmpzGs7k?{l$x_vKl~P=i$gAM=y literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/si.png b/lam/templates/3rdParty/pla/htdocs/images/countries/si.png new file mode 100644 index 0000000000000000000000000000000000000000..e8f874309f6218bb9a1e7d743d1cbfe3d421f2ef GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF@9vtraN(Ai zxYDJ|_g=buN6RoUyJ%8z&FoiiKF?XQC$(Z(dFRID^n5w%0tU4-R_!cS?MyDcbOEyx zQS&@SwbUgMgv|2fU8?o`8;pF*EJEt-VmsZ^XGN4Ri>g?>bo)gHy=-QKJWkU> zUaN9h&ldftDF(rfW|5sv*^4bAS}Y@5+_L8Qm#q#cUE!S6kZ_JO{cE7tEl|LViH=g*(*lH%Gf&AU&If47{#4q1U6N}|U#r4Fb|9x_lm zW2Sl8O8bzx_6%K>#YP(It@LksST8o!dFW%e%gJoLt?^|)$Dw(a~ODXZUvr;dnC)HF`bhNbm`}gn9pFh8U|Niys*Uz6nfBg9I?c29cpFX{N z_wM!U*H4~2dHC?*y?gg=+_-V=+O;cJu3Wlw>B5Bz=gyrwd-m*^GiU1S>wz%{jK@(x zMhL|C=G_KrV@&dPcVXyYmGuB}4tTmahE&{2PB_4{>|+EGl>=7H zi|uQ_nty1@%E`a0t)eBO8=ZJ{?2etOoxI#{=7rl=kKMS#DH4?YDfMx|rp;H&&5AyM z_*BBqb@Fpj@h5Ihj;;<*4{fcJUW~0Vu_;^5`uLpH)yb8SdAoLPgVQT5t3|U8MLn9t zcB*X^SJf_ug}b^YEjsk*(xy{Qw`RRs^`~e-KvBhn`h<)Wr>Zi)G_Rx_o&XIk6$J|& lB|T+xeQjM+O*^UCBuvHR%b`zJ8yLqOnF|A6DZ{-;7h&m^TB;T1ViQhsL3)(uk1 z-wqwUyl?;0OP7B>dp?_Ac8;>~R?moeS~i_p_FbOIE2}2-$5m8^mirh-#|M`My5wfX zv{z(LuF9LzQM({Nr6Vr1DnGT`*EJ)=C*Q$5$<-;x(Yx9%sIj%JgUh(cGC{h}=a)Y-rfy|O;k44}2R?uO z{_Mr86KBq^Uc2t$lc&dyAOHU8V z4`pSqD9B$>Q9P@txLjI#gQDU)ef>?!%6l|4KAD={HZa()rFC3S?~ zJ9o;IDfRXBz}N)F=_nvF1Y~x6^93qpO!9VjVd!9$^#F1Xc)B=-RNP8VIKY&4c9!7b z8^(#BwqypYefwTk*3gpX*FRN2TsUvORi;zCc(QrG2@S2NEmNo4Pu0J4?%FXc8zzk> zQc`O1x(ZWtMPIyl`B+?tN5p9RrVx$X?$eVv4GceTD==r}`uTg8hOzf4u2Za*)fGD} zEg4!(OhTu8dDF`)5_3=h{CMxD=3JPis1Pgg&ebxsLQ0GD-I AMgRZ+ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/sl.png b/lam/templates/3rdParty/pla/htdocs/images/countries/sl.png new file mode 100644 index 0000000000000000000000000000000000000000..beafb9ff4c972eed4b2e969e3b4b20b86067142d GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzk22B@ngqN zo;v&c&%d9){=9nq_R7`kr%sj>%jFB#Dwb`Rj_Ob^Sgsh~uUWWUw{(q0#w^qJV|s;43`$p6H0?I2 zTxZ#^-K=u0+q6qgo%{Xg-3XX-&2Rd7uL(z-%I5|2?eS_@5xd}0V8^D|X@_#w+|6Eo zxp4hmhM4)xaSJ#T7s{4yRZ5wx-*!YRcdl98MyIJ4-6k9jm~%OA<&_t&USGR$^UBq$ zi}C-{&gDzI=094?o72PWl;8Hur6n|ujF)T;Pq+{_o^2+jS&p)l?`l{ z4{DdUPm>I4RZ5wwA3s?)dy#4B8mIh4_GweCA{vd7d#t1CEE+ZjHm{59-JQ^~KDuvr za?hrWS%*rj%NSI>7#ylu?JHTmS_J%BB%&rMT1H7^&a$Z9;MTk~pmlTj)B{;ZPaOLB z`}>=Bug+gQd+H1@ERP*Me(1-quit-udH3PMCGTS690 z$WE;+PA>J03=MX!&5do94(OP=YF{nkw3Y3d zyVu9K!70a2W{!V=x%EonQ0j+%bXl6tn6GHyI{hm9UT)k%vjR2 zYudK9u0?xz0z3{KIumo{(k;Ks2k(WQJm$qUyPUynsiF0ivsaWsA>rxj=d#Wzp$Pyv Cse)$! literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/so.png b/lam/templates/3rdParty/pla/htdocs/images/countries/so.png new file mode 100644 index 0000000000000000000000000000000000000000..d087512609669ae913df487b21cd822f22cc075b GIT binary patch literal 1033 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKYQuAdhTki zyw$n|YxD|M>ldyuEZt;KwAP?-jZw*Z@RWx2oQ%9$0Q$x5Kt-qjSqX zy~ui}x-Fg^2Yq{vdNl8_N}p(+)Zy2$Cv3_Y|BgN0O`9F^XF3mFq- zIA6N{QQ3yaX)}(LZ+KL;{z3Wr2PG@7S8saMu=zp5mIrM+p42bc)4AgC_RFsiojCjO z;lpd!t^p$j7&W7S)*;}qrspKk4#p&JcNc~ZR#^`qXS=71V@SoVKg>)i%5u9Lm6z p`e9>Z?2|_?bCOmp)M!0x&2VOcX7sl!<%OUy^K|udS?83{1OSMRQjP!s literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/sr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/sr.png new file mode 100644 index 0000000000000000000000000000000000000000..e06354f741ce2a2a9d016bc65715d3069bb3af19 GIT binary patch literal 899 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRS!3-o%c^#X@z`)E9;1lA?!0>+*jE2C74S{02 zskeYxf~h3PFZe&h)9nUcKpta~x4X;#rR<-M0y)Z_E{-7;w~`YWnA`*s5*iF8B@%Mj mEQGncBN|-w+j0}c85r1;7^LNAC*K4rXYh3Ob6Mw<&;$Tur5rr~ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/sy.png b/lam/templates/3rdParty/pla/htdocs/images/countries/sy.png new file mode 100644 index 0000000000000000000000000000000000000000..be08e92cec7ca2243b3445bf1df271f57527278c GIT binary patch literal 920 zcmeAS@N?(olHy`uVBq!ia0vp^VjwmLGmxBBofpW!z|0Wf6XFWwGW=(7a&l_<-_rBH zXX*c?YyYp^`+x7T|HsaqD?SGV|Ib|mg8$dsvqNYGYce$QgLs1*6hlT-sG}F^Vgry0` zRb*^iTvWRjqhCw=pK`NS2CYDhuhFK<#SvHPUK7my}o?S7CLpg<0C6!YtkxM0w zS3O-oD@#Z_Lqso6(x^znAV=1$RMDzZ-n>v+H%iUER^7H-+oeg#G)2)kQQy5y-=j^< zGDE{U+sL!k)VIsPtyar6SKBV%DyYxExzsLfl7(N3nOCh- zo?z==;}JW_HL}MuZgN2SeE-yW{%Q04Qf39F&kIPM6OcB?H)&cx>dch#m0@|ads-9M zuV`C2Gj!kTv==8+9~{ZjC8K^K@|xskoJ#aDd4r#k4|n$a#|)r`L>EyZmOGT)ix(u_QNOdCD5eRauEQ@7eENe~eG#O62UMC-2iUrbVr} zfBE*>a0ZcAC%XB*aCUm?ttt9A>qXvVRuQdE?r&^6S1ek1^X4@xYgvsc67Pg1BJ3e;yGF*(UEPk`*gXsg`W7AYEk5B_amky4!v8I847m&p*$mA2%&Zlxtof{* zg`B+gyu1~>0+j+1^%Aljva+qR@{RI}9g5Ov(nvcZvGqm-R8Rauk{aD8xXK2AYfI1V6A|1x1wpciCMR)$0|4H z*-nA$1N>L|#Z_6EhqC0Z;3-`v(6m!w(g~wgcY;>j2+mr}Y!<|nvy^SoRoBvG%$2J+ z%2#kUZxL?ZAU*MbO7S%1+J#yR&s(&wHkftTx__(ry4#-fkGpKS7rgFzz~a-gQ+Kg0 zJubQVy212K`V01$9(v-m`lQ|IH-Y=^`ks0jI(rY#<||r#xF3IM*-Fl;JA9_2T+1B$=lt9p@UV{1IRh( z>EaktaVt6D08`bj-4(BlTO; z&zhXrpRBD%mrmW)bu<){~ix7KVN@uMy|q7p9?k`S#nEDYR`;}m^GWB zHO*(X&BFE9qkOy^osT<2$BJrPxq0#4wOf+}#UzEN3&=~$vWlEKefH$3GkRJpS7feO zzfMb^nTxfea>vftpuIkHsL^ea!P@6{mdKI;Vst0Lep48~^|S literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/th.png b/lam/templates/3rdParty/pla/htdocs/images/countries/th.png new file mode 100644 index 0000000000000000000000000000000000000000..5e15d0e26318254090e78c9acaabf1e625507bcc GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFH`Q3~X|vnc zX>D5cvHFsGT@4T0YwuJP>};&sw|v3lwuWtUr(amR{L%WAk5t8%sE9Atm0zVJzeHDI zsh;9;J;kMZN=prtmm8?8Fi=@)thUNZcbk>&dTafS_C{N+^;bKXuJf@!;%2qQ+kRhw z^I;F$O|CXe?9BS2{7%IMT?+Nu9UF8e)O%-A*u^lPEkT}ZV}lMv`ftlgxSbMtIy>Qd zTJ)K`q??K12lJ9|7N*@vjXoL~uqG?vR9W`jqV%f;sh7&K@04ZVNR8Q9oO!7-|5`)& zosPOk6@@!%%l36No|r!2T21+ysr_gAIuCAM`*z!gw-?TRI(hv0&Fep}U;TFH){hm- z&MsYiq9DKh)X66c7aU(O|JdeDHx3?nx@qI(Qzu_enX>lWxszwlo;Y*nczu1%&!1l& zKD=}7+QlhTCjS5b``52;@7}$7{`~QkD;IX}-tqnWml_q}YGvW0mhvZURgT)KT=dXC z<70X^+U2b@R^I+zsjvoZTbdf0`$m=DeQx2v-*l&39;3;H>^ zVO>V_%bu!Tg(=6XaxOHNe4X9(bZXnLB~!jEob>PCzdwKeeEat8)2B~QpFVx^J9z;Sz_Bv1uolDE4HLkFv@2at2n z)5S5Q;#P9P0VXak?N_&Etvcn^)#ZI!d-dx^Cr!!9pOKb3Jx_MN%)A*nbEU(=j+PS_ z7VP-&V#S6THx4|QP_bY_+veTf+nRQCY}vDF-qgj-tN|z5?p_TD^oj`bb3K0N;=L$& zjUx|_tvaTnrg(ZyX4dnR>=Xu(B;!CaQ4xVjftQT7nOsf{Was+$yQs3Ds;aX1H~WW= zzdrt8XkB)8hH3V*FDoaj`^_*(J@&@3xY0?M*K}*|Sy|iKzkBb-&V9S~#vW#d7q5*1 T7P)&|0Que1)z4*}Q$iB}W;&0} literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/tn.png b/lam/templates/3rdParty/pla/htdocs/images/countries/tn.png new file mode 100644 index 0000000000000000000000000000000000000000..f05f1342769f31e70908c574c88dda3ab067c558 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKXd%V0R@Hw ziVXV|87f2>4k()t8)aD1FvNeYv^fDl6s3e)^C6^yix^Zm?6kc7FuD~S=+2a_0VX(oTkMgL`>)#tQ; z4{3qNGo!E72&n6SuyUwI^Nu>bulzlyZW!@U0F4WtZTnYb6}?`f$vU-f znbj-@Mg7!7zGoe;IQR1IoH?yc%F98~Hc&21@8F~(SGsbtnM*|@7fevm=eC}|qo!zA j``&%iX3a4%d(XhI_^0kE+ta@UK_TMl>gTe~DWM4f8{1AX literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/to.png b/lam/templates/3rdParty/pla/htdocs/images/countries/to.png new file mode 100644 index 0000000000000000000000000000000000000000..b53e46f1afe262aba5a6a7a0844af71e26adf1f2 GIT binary patch literal 649 zcmV;40(Sk0P)K8Vb-H4bUA8&m9fM8x7JR5Xm7D)g%;#5faUQqE{PVHDs?O9CtUs3d3Q1o6< z_Fqx#T~P68UD0b}eoII8zq;MY$NJ>r#ti|+5Cg~;4agb|$tD`lD<9J_C&)1-kRuzd zGb`LfIEgJN&`Ch&QAoH;Lwz(a8g-OfA0$JyKnC={t9%?~t4&D0|_#LdP;8z^M2Ajl4Kkt@(3D`Q;^ppY`qMG`=R j{K7mPEDW?%uz3dnqM;>sG^aEs00000NkvXXu0mjfCgBt| literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/tp.png b/lam/templates/3rdParty/pla/htdocs/images/countries/tp.png new file mode 100644 index 0000000000000000000000000000000000000000..f8b4dca6c48b06feb0776fc3406f6330db5fd46e GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFpKfl`UEffZ zmFs5fxM<#jHLF&;Iyhgwa!r-L%UK;(vf3}>bX?Bmyq4E(J+I3e z0k2IGfjcDp*2#wMR*c#&AF@R|>6m`nDbwt;7Fmbwimy18Uw5m%>DzF}v*Mh8s zcx~_gecyh5fBNDXLkR;z5d(7xGiwDaYZ)tN87D_N2S*wQZ!NDtoq$A>glw0rM1h22 zx1xNDymY3tQn9jPo}&INeT_;D(>bPEwOV>jdIoI<7Bei&CYqacnK-RjMJT1^BJ>vv0DGTo>s#)h~2GXvVILj4c^?d-JZ|xcckQug_mU zuU)_P)$3Q6uUuAbR59x_b?<@>@Oq~B`bToupl`q%GuU-N^fsS zKtOSRet&oOmQ9;BtY81{-@iY9{`~s&>*vp(-@bkO^y$;Pckf=kdiC__(C%M@7tWnKcjnBQQ>RV=BNrIOqX1V3m|9$043uL`@^*J&=wOxg0CEm` zx;TbZ+)7S3z~nV+#;PvUr$<Ztm>H&iGTifC RgX0v)*PgC^F6*2UngGKSUiSb1 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/tr.png b/lam/templates/3rdParty/pla/htdocs/images/countries/tr.png new file mode 100644 index 0000000000000000000000000000000000000000..0ecd06c99662602110314937b765fed83de71f0d GIT binary patch literal 1029 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzhG*1*vk6X z?mZvBeS7r$*$D=Q<3Pm7aEyVWmVu#$f#C!r<8emDlg!K~n3<2Uu$*RPJ;lm;f|d0+ zE9)6{_7j|($2mFAadBPb<-Nemdy<#8gNy63fWS2okyBz~Hzg!)NJyNMkT@$LF+ou9 zn55(#S=rmNvJVs$?<*=^P>??_FF#9Ca*4F`BQ>=Xs;W=5wNI$4uTfNdrLX@|Uw^%_ z@)AYGGuqnQ)YNVm=8q2|RV%9UikZ8@bvr?9{x2h?rCJ?&yh z&KJ$i#|sM1S5@7eG2_OxX_pr-KDlq-+ZQi@Q2>mHQ9%C?aOHQ91=`A(9Bx(L}>`)=6HCDf2_`2jnQMp>09Q@Xq-b$Jiz@SZaiIAbVq#8Bv>xx_IuiK{lUw;dHv+sdDHQNQn{ z{n$_ck)Qs2bHxp|YL~qAUxt|OaM5`gZgtSp;INnBjS!2IekN~X>`wcey-#rZoZ@yT z+TmKH-RE?#+c8f21FgSg`aVhVe3azzJvZP>w*QNCpU*h~uQUBVzP2-XhC&TwCzEt>Otwg}qL??_-?Sx*Fe)cRL;J z_NyTBb#D0C^pLY@q08fgKlRm~Zz(@qTkvjn@54#0d#ds){XPC~SafIplyg(Nf9%=V zmy`Bp_ojPW)*O^zIHAII+Cbuph5YM4%S*mipJUzbM!9}S4fs``bGtVG$i&X~d$-=R zR$FPHICbL0;=;nz)YQ1RxWK?bKR-WTUtcdTFAon7CnqO6J39jd18r?>b#--FSy@R* zNg*L2US3{SR#s+aW?;+$<98I`34zd1DpeW z^;a%z%0DE+`RrYYv7{*c8)|jp3uv%pwTEkkOtPiyuTRBWEbyjX>t28j3$`FS-EKRgnDyGH& z6LkW@0Texoif{k{xp-1R6h%=`!#jB0TDMt7|3H61&-OmgetSQ??^BhO7;_AH28lo* zj>W}dANVuGKfy`J{AQ#)EqgSrR*}ip0As#R z>n6DsY8Z47TGbqQnW3HK*|tamEv3WEggwpUo50joy~$nF+QYG|2prpzx&1nEUqPF^ zkk?x`(vR0}rbp}UP4ToWN zk{SRatF2+g#jz~CSB*7UmiSX^O#P(9>FaXucFb;q?wziApV;mpiXf60qJa8x#VAz{ zSM(aIG}dnw8hE;WwvR(qW@O5m#P@{gSTV!Bbyr6xz z+eag8)>4=msq39wZ4zeUMbeiFLdTPY1xn^_b@6uyG!W9ZXhqPwGF?2T{Mu#m-Mju~ z{r&#eKR$49JRj{}2jK3-U=fu^!eafPkHg+hrp)2fb%8f1(OJm7y?d}|R`nw%^lKn( zE#V`k)QrtUW3GI=HGN>d%C)&9zaIWXk2ebPmfP-6yYNU<2oB$BvJ8a-%|xzaQjigh z!iAn}L|?Lg5_(RB<^s>21)>-q*mf3+IWii2y^fx;K?p*wqB5N5bpW7Y!!JJWOF11# zV_eXjleS>q$O^qG)YT~X;3e*g%^u(JRF literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/tw.png b/lam/templates/3rdParty/pla/htdocs/images/countries/tw.png new file mode 100644 index 0000000000000000000000000000000000000000..2e82e66c8f9a0bd5ad2351ae364a009ba55f0a52 GIT binary patch literal 633 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcX*_=LCux&QzFKg7s`D&hvu8_hsd-nwp(-cDtXEwUUK%B`f!8Zh>tAqFa>I?kA+Ydhv3{+U>@I7TP=} zh9XvT`WM)!c&YK4ty#XYw`;kI9ddWaH~lR>g&jPIavDTCbbx8dhoNWs7km;1{9cTh59&W_qHvT z7IOOf^~)IshO>-}XBZjJF*BcGX1>76dWMztB0Kw8PR=V_T-SJc&++o!5D+*oBy?Lu zknZ+?Ei0|S2s1bhq#_?waf3}Ik6V+D7; z3YP;V7?Zr+T^Kr8Wj%nL<(@8%Ar-fh6Amz4+4?nP$yeW_kveZ<_8z|cxzVYuD@e@A zOKp*l*||7J%}P}VMOj@>N5fEM!8CRL|4-gLl36gJapTNov)1ioS-m^oCoR!YD!{`p zE;7u`(Rl`MNjbROF+Xth7FSk4R*Xo%3X>~muU%ry z3J6N!2++89S?-n60>_JqjSh}6@GtI|@J>*}T$8)uuE=2}`7SzDLb+LqhfS2{V>IyzRnxHP!C zx45}Ad3kksdbUN!EC>mil#;q8JbYSo^sL0h#VILEgF=_Nx_5*}t_lpE9TGM#A$fg5 z@;X2NUQU0-$VoXmS@v0TDpixsLwi#L+T%pRR5U}4g>0mi6AUC>loWlmyc@%u3w{0T zBGYFV6)vjIn^#e^xVCt4Er+3iQM|Qtm7jHnOX8I5q>0(N^ULka+~gJH)nX;1+I*}t zm3!8f2ImNO&+#j+QIL|7$j#y@&gWmgAfUZYp*)d&VwDmP7jsbrtFF4p+8KeJl*v(MATF{I*Fa>4@WwxiQAzA&+6R=?sLEp>TKLn9p&ovqc=ZFTwgADMLR*sV(~B?~4r z?p(UA+dm`DKRP(pt;SF~;KZ4z%eDq9_iSIh(@v>3GEz`uN$x}0IcG0izd1=V`oKaZ zJtmP)JRO>E-oO9U>zx`jIYeBTg{$@Xw(o{tT3p39u0G}2$@Q7B^;A@~WYyxJh$%~+ zrmD33sBCarcb7+T;?Ff)b7D3r|tZy#0d%_Pgg&ebxsLQ097UT ANB{r; literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ua.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ua.png new file mode 100644 index 0000000000000000000000000000000000000000..db85d97e7382372366f5e5ed666876e46e0e0cf4 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFcUXPKarGIe z)n{DRo^#u9(QW+&*EMH6)}Hm=e8q3uHShK30(RU8+I8D^!+Ddk?udQ&LbhD8uAdTf z@L|HyCrKw>L~Xy~(lIyV%xkZS%To{BDZKotVex%OCHEeYJ9bw2;tiu? zXHD)ubJ=&q@x(dzH=n|8-4D9^IO5~?q&FXv-hR%1^Re{IF^01z7|$GMJa?S=%rWK* zr&!M%W4(Bq{p@khE9bbbo##DwlJ~|Xf%7MYZeJC-cT?ihS&4_YWuM$rymDUt#Ur)l zi=-g4Z~8Ed-QmI(!%IDP*5-J2Kh9XxsT>fzhR zFY{`Mh)YWfy^t3Wk&zOV6&96a6PabaQ%BoSQF;BA;zwsZ@9Oe8EWCNu^5nLMw6XObm?*RRI!i}-FVdQ&MBb@0QNhZpa1{> literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ug.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ug.png new file mode 100644 index 0000000000000000000000000000000000000000..5c87b921b79ec50e75223ac536704a22b497025f GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKR##P-iGFr z2TrVAzINNDZ98`F(=pKT3-%2P545(o^78Za^z}&1NMYt+!8vFImRCbvygo^}JiR3+>t?QduRqexv-g8-`1ksU1J5f8?0{`i;7` z@7e6!ZG7^y-GYTikDhqlzU%Y!dEl+P0oQH>+`1ii>vrmyGYpHDFtD&NFflQlJ;QkB z4C93htY^-!Ub(`xbQ$NlbG$ch2y}OI-Mc4o>5{~gCyG0F$-aKAedC7uh7F2)_Gohuxn)Zcm>&{r~U#?w#kGHy&57IsN+=@a>!5@85w(k2&7H z*15S^YgR7aJ173a z+|(m!>WhOEujT2#tFyXYY4@YY>rJQ6_X&Y-x&zKvxgB90kX&&w3l;G)}9Tboq z99W(hUz?Gp!Of|`%N^t7)KyXfj9Fm(jsiR(uuMndDo~m+$=lt9p@UV{1IRh(>Eakt zaVt6D08>wI@}x<^j~6NRC@C#|oGjen^r%ScSC!(YX>F4(@p&;W+~uGs{e440y}EX= zFn6@O{`~~|1p#i3-gRl2RasGyp<#Y;d1e7CcJE)mckR^9si}`z&fS~0j&H$*H8X?N z&(1r1?wC#fyDyTLm-(=9ef-G9_2F|j*Yb{nqK^gKg$%7FQ&LrqEJ+a!G2OcL%GD5& zMkl$lXKZQLzOB30=x8@DPw$+=!mdY$7ESu}s%_b`W50IYn&p*qkCB1#yWwgVS=CP< Ok9)fMxvXk?Dmb@g7Xs=L>M?vWN)|mlvGlYOwbXm5Spd0X4I!lDyt>5B`ib}U_U zf5rL-3EAsJL&6l zIxz5AeEike*iQuo=aZ5i<>r2`uKrY2b)%r*eSQ7e{QPGP4Y#YRe)ROb?d-f(U46Q& z^v8q=hl>i&RaSnQFyTUN?c?t5@3Urpnm&DZY3bdbp55i;#~bQD%$|LvyX$CE)2q32 zek@ydrnB>CYs=y0=9hElzL-1rOn3K}<;#z>x1H(j{km$^wW(9@&zbXM!-n^(R$ZSx z>+zB$XQoa$K5^o)i4zx;mY$kAbxnQ!$IY9cu3B|r&g>(TChh3#ygYB-**UX6?cDKW z@7@<1H-6o_=h6Cg_t&iXcHqFrJ$v5l-u?Z=@n5G;1LGGM%cB5m2xu}qEd)w1CV9KN zFm$lWdH^{GJzX3_DsCkw9ANsQyJpLol2G-s((isVZS(&zEts%#>DrZ>rQ!<`0z5KY zLULpRJZ_(KJn!LtbpM)7I~UjgIwR?zm~HKzEX})d!-o?yTBh_|Nm(#qVdvgujk}7f z{1PjJv!lIb15VsMd->|&R(mTuO>2F7b8TS_kq6ITKYluQ#7#5LLiRpp~ zNolE%pF9+O`b_Hq0~e=vx3`za$rGHK8eJMExlb{*T1l^3Ep=PnXSQ+qwJV;Je^+g& aWM|-rYYv7{(!MYtH)6Ty2>j%Ccq4t^8E0mQE)qHP@WeR<3ewX|diP{?x_FOc!i9m=TWKpuy!WasLzB^Y9My$R=-qnloB=a)8_dSD) zn0}~qGv!T^+ICqRfHn8PPiWoE-7+z{${fIiT8{|vG61#0xb~?R?&hdj1E^3jtv0%O z0=-BzGda7#{;J08eh{(CVH9_rFVE7H*=+kJ-dwZLN^(N}NsSDkSLV=4a#@{a zT`k1{r`$cKnKO^|Hzc-DPmZ*n-^L!B`1u>AGVjF8fcf3L@D`*|nM@`cjTUY;-0y$= z;{%8HylqQ`clQZfXM$1VRI%4ew7Nu(v zEJPFqS_p|hrr96u>bO{;Q3>$PgZLNCE9ZxtPg#z&!ldbYBPCI9D7;t(qqaJQDvqao zjB&Y^PPw_Whu9yYqLD;WKj|tL)$+y@8abyEcSnkjYl?se_gzq)Io^_YLKRVqWGITM z)Rh$_w+M-gE23AY+$65#U;dqu%qcp0DggnHTsc~B>&&A|`P7Q!JLxgR%%rM^b|S%B Wa2DvR$=mz)aB|wU)V`G4HGcr5;n(f} literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/va.png b/lam/templates/3rdParty/pla/htdocs/images/countries/va.png new file mode 100644 index 0000000000000000000000000000000000000000..0fc5b5fe82cbf8195013621fc5180a83a5f3cf85 GIT binary patch literal 1042 zcmeHE>rYZ~0KG;mWiiv7y{2W4`IyUPTO-|?I!s+=tTdyQbEVmIxwe|sGMV<$X+Elz zMdTwLDrrQ{1g}7aTn9fcmmA=nPV*Y5fRa*CGLYzB=yPW~=j?nqU(TsvWu?(+TWJsk z(KFJS$9*2`<9dIJPfvlcdI+LGtjz3GU-Wvt{aoH)Uq7zHd%An&awUf8XJ$-V92dQN z*CP;oHIsLq6b(uRm_hlpl`{Z$l;3C9K0VRWU3%lzdC~i3rRuE`kt@|L<5RF2efHV# zUJb+=jhrCVZugQ-H)^wyBuU^nX16aI40=r4q*reTFvKZ?IM*OlO3@4l7?t$eN*aJS zkh(D5!!1+VNPJ|E{YxC57eN{r2%M-KN_5$eEzhz%%LTs|3xQe+)UuyH=Dn-m!5{?0 z5a%kSk@x|zA1a}MK?)F4uwg2YP<7IE8cBd&PD6+2C`>1m8#HjBb~soo4W2+kFxf`U z@Fx7j=Ev1R#*u9%U4;JAcD-T;fkaM%xOrl~s56GtF-V*-^d{ahlQK7!G&h#KFqytQ z!_p&%opZU)`COMR&oaUG*bBT%7k^m`z01X3NAcUPD&7mOWquAz`%e|DCG0`zWaP9{>Q&9P*W3X^fmp?9Z6$C)zq+bUyx@ole4dnpU;=C z=|5W+TYKf<(S@6mgz@LLlFF3RP0@G7mVIQ|ByX{hUiTm=!lQuQbqo&WU|GN+zl!jj z)<*Wpbj-Fw(*Hd{px5!DAeuQ~&cUhe7@^z~R;)`rA4ER{KFby+!Gmx>E5 uTn06&qfOBLs_0^|nIPJYiIpWW2fjg2dPUq62rAwCcan_MEM{Lye$^kEC!mx7 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ve.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ve.png new file mode 100644 index 0000000000000000000000000000000000000000..0a90e4733a304618e8c9b7457afdf835420d1859 GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFKWyl{c+#b~ ztZP{%chd^5aY!x{R+=KFI#o)0skG)y8SUAM1}jvIml(Mm(X-p2Y0~HzaM;oRfR+1d z*N_7qp_@XJPbcQz2}?d4op~lY{YY@!nzF{r%BCw43vV7fA$;Zx!?|JNPUAxA6yEAp2vE53WD|LK$IxpVsWADBFR zXz}Zp)1NUtncD!^(P=lk*xc?+pQgOA-rBg@X*5Is+s8}6O&&~PJi6o zo;o?ba&!Cd@Bc3#;G3V{&wzlqIH2wSfl)jPaD_kz$EG(xImRS!cNc~ZR#^`q=b)#H zV@SoVF3p6n}rTHJ1=@P$++lKgVV>WTc5VB zG@K~t=$UzwRrRQYV!C~Oym_>Cwl%wYxxaiq|AGmVcCOsh)3$e8Uw32E#;%2O0VfWf z2|cZLASm{-;f2#jJmSSQmS`!etLUieYAdO&R9m-3gG)pxB`G;EE%o_>XTlGjKYjR! zh08ZHKQANe^&Erbb9ZK5R{Q#$p>8!nbV-GV!{RyMyP033( PgM97j>gTe~DWM4fPtHn3 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/vg.png b/lam/templates/3rdParty/pla/htdocs/images/countries/vg.png new file mode 100644 index 0000000000000000000000000000000000000000..1ff7ca119ffbf7a822ff05d53171546b6ec93ffb GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzq4S`-jL`y zfnisRE3VZvc7;csShixhiS3=@niCoM>yt9C*EGLazGg#H+X`#<0(Je{^&Pu9CY)Qp zCDqAmMQwYEi_g}X3+884%uOtslV7)M?uN5lj!v!WO^r(0v}og=O(*n~BPMojhz_fD zu*t6|@6a(ww{f5JfTcGPursr60;8JB3G}+9n(J5k;oB)w2b(@`bc> zMD&W~%}bR`)Ac<%4O}WsJR23M&*!YnS`oi2HRp

1b@MzhPNv&yI5c<^e`t}B;rJ)U;x z?d-*?BNjfY*!g+sxvvZN-PnEWN7IxgJI_7Zd;CJq@*8DqZl_M!ojv<-``%amOSfNs z_HFZ_^G{yAPo28EeC@^kXRaPUb^6rB%cswr*|d4{wo4cD8|s&y+J5==^+zwBKK=M| z)1hsvuWVAAWbC^vkYPH5*km#JK7}h+uk^0(E?g16;^f?8XO7I;-PFA$XZeAWeP?Dp zetBu-x@i+u=*(YdyKQg#v-h13pG|rDaqaYZ!t^X95`k^BX zEp38VZkm4h67c%1@2fZddk;7qKIXT2jobAbQLo>oJbjT;Qfl?+ap|kKHTNHszkbto zn|TUehw)xL01|La$Wckk^!ee`+!#N+PWxPu4V zs;W{RK0N&O>#b|oPJaCO^4z)OA3wgGGiT=AyLY~RidnI|xT-Q_-+HaR8yz>Uh<*3= z@w2PFmoA>&H_N5FtMkf=#*J~3mlFI>g#~~6_U+@xj}IR{ym8~kwQJX|T)A@T(xnR* zE}TDq{@l59XV0EJbLPycQ>Ts}KYrxMk^TGk@7c3w=gyrQHf&hFd^s?3fl)jPaD~7D zp8L)~ImRS!cNc~ZR#^`q=b)#HV@SoVuM??kZLLUK~1=m8tcS^PI0$+H`sE;;GB?qp=D`LpL&C2L&PBnL%S z&fw_(IbmgHeaj4(1ruEtOqeuf?o{6>_2BaG?u|3%tg#C4aJ_Kv(wV)DGnXz7^N$Pm zmejZseK<4k;j~LPp1pj##5VO>%z}XXOJ1t=@ts>($N5NVt?~0S%v_QFv+YWo76yi8 jwf4N~xw-4}#!7aE-`|Wp*g8J#2l?95)z4*}Q$iB}HYKov literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/vn.png b/lam/templates/3rdParty/pla/htdocs/images/countries/vn.png new file mode 100644 index 0000000000000000000000000000000000000000..31af3d543ee3d420db534b3efc4d8e70f3a41ddb GIT binary patch literal 1038 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzhG*1*vk4> zdd7uoyf^Pj+)nPcSnd10r_j)2ytg zSXoc7vK|MLoSetGIL~o$UEt+C$;;cp#dTRg;FOTi4GD>J5)x-6Bqj(7-jS8PEi3y# zQSpL;{CRo#^9l;HBqbM1OJ7!3f2ys$Mp5ylzWzF8VPy2f&F#CJ z+apJZ+xB*k933AzIsNqVI&5wI%h&g*o7*c-&lesZzx@4w1praNJ0G8qettg!1HT6b zehCQp78v+8AmDq;zhB3+8-G!lpRn`N@+2iTr7*cU7IpF})moIxi-+J|E?wL}bw|5U;@@{ncrI)#u z_vXB;Rc>bM7&VWEIwf+uZV8BgFzb zcCNLH^Xr&D+1nvt*A^RfMvW_%CcI2lJ$?ApIo;5p&;uguB3VpQDH#gKjww!xkDVbU z%_*{qv(VsL;KQjC9tP{3_Uhqd<2q^`{bH8p$@X*&ttTojt-qLCZFEX|a_%_jX?gT@ eb9_1YOqOAqi*7;Ukpudmu=8~Fb6Mw<&;$VDeHhXJ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ws.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ws.png new file mode 100644 index 0000000000000000000000000000000000000000..654b8420e391c68e142439e0da4ad6e794ca5463 GIT binary patch literal 1027 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFza=Yo!PIO! z1K$ot{vE8sd)P(y^2#0+kh|#U_be{!S!&^C2Ei@NBAYoScPi*hvTo#I+{w?nTbOf~ zlEgt(>4Q#IyAvay7iE1+i@n6fu$Pl@Pj%HN28LP&h8kw(GrYWKxwtyy?gTft%VA+Jyu5a#r2KIASeKFYH#75VX6EPG+TX>+Ps__6)zlsq5akGxKRy)>Evk$5~m=u(O}wuTxgus-||sK!1mZ##>XRe$w6yl>=p5G5JD{uk-pcBvk>N1| zgS!?M=S)ma8ykPNv%6qs`qjzls+H9bH@EL@Zny329y&Sw^z!=U>-*ZnB*B&AD+~!t~YgLY?_6+Ry8<%lDftA>)0tJoo(+JFIGY%6^t1PP<>Z%xKzW>CWOS`xhOqjK7-p-jTn|OAYS44T{mI?-V!~{8c`9wrbFxJ?( zVbwAVZjB>{&#E3cfBN`A7NZq&ESWW~gor&%On&={+hoN;C3{;*jVlt*Q}bWGVK&sT zHWkyD^74Jk(-)FVh8n7NTpA*;k}{vmahPeCD{HFp%r0lhxTO1o)yJv}6nLJlelF{r G5}E+2sQB&x literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/ye.png b/lam/templates/3rdParty/pla/htdocs/images/countries/ye.png new file mode 100644 index 0000000000000000000000000000000000000000..4c547ccc92f7be42b12f4f5cfe6ec302b79e3531 GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzk22B@ngr8 zl$Ebsy?XG_A$bM)+Pd1N<|Z>U)7;#g=g*&=J$vTIWjSmD`zfJY} zo)hq{ApB`g$fNAwkEJm`nzA00Cw*zj`}_IhpHCk?K7ab}>*tRzp6!=qs1jkQ5Mnr} zz$kMTUCWmTFO82(tjW0wAsz*PK4u^4FC5T0bh$EKa|A1 zt4h6aVq#)oV4$zBucxP{rKP2zp`oa#C@U)~EiEk} zAt4|jz{|_a$;rve%F4{l42&aSOpOAnhrpvf&rN`aGA4PuyD)UH%6b4f`#oJ8Ln>}1 zCmdkvXlG0dmsXb}OHxEb8=cg``102EZA)`o_Uu~OH@|ZZiqfjNvb>g_+=bqf zm7?Olr3)skZ)n_SzQ29J?&Z__*UeLu59pX_sH3N9{zp(T&X-=9uHEPxl*4_3k?mQ_Uw58SWP+)nw`njxg HN@xNAc4R~g literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/yu.png b/lam/templates/3rdParty/pla/htdocs/images/countries/yu.png new file mode 100644 index 0000000000000000000000000000000000000000..9947f62dce77cf45cdcccd9d66464376dd59192e GIT binary patch literal 1042 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzFzk22B@ngsD zJo>a`&HmkouW#LR_R5|2XD&ZsP_JT;@L|!aWznkP)T`q$YT;3dkae1*8#vz~d7Wdz z690l7-dStCGFJx_Yz;2nk=k@%?y}vBRv%cq?NmqKoLvVmUcLM7%;m=nnpF&vKFnG* zth)8Ay0yF}t^6iU0u~(-wjF|6@lp=`iXPMCow_BBGc>&?>4z*djasJdT4ND4&naz# zk#CDz_GYW_e*eN90mVDR%eKYU?2aniklMH>tNrlFOHViNI&tOd)#uNj-MDu3z`ngp zG#Qp^GcPmXTxZI=(?xcJi+s1a^fU+M6Jh!b-L=;E8*B|Ty_)WHGTrS?k^l9=fYq_C zXHK5@_5Ito&!67Ddinar^Ow(_E>dAwrpdZQlXZiMz#2=5JuZr?928eMDNnLeT<@ka z&q?ESnCS{ny`4cO8+;8n1(@s)v$&q_b|l{Ec(Uv5{D3RjehWhE_NRGYEs5Bg;&Zex z;(l}bwTAQu?YY;Qvo4)Gd-mjsGbfIpK7Q=qub+2s-nf4G^6wu%etiG_@87>afByXX z_3P))pWnWH`}FD4yLaziy?XWZ>C-1qo;-Z`@WzcB*REZ=bm`KC3m49v0~&Pp%$YN% zPMrcqA}}gP0jVM2z4V+HP!D60x4R2N2dk_HkaNJ(#WAGfR&v4trYTdu>b_a)yXDH1 z5Yy7$Giw{1?%YZ%GgGRniP3u1)V6Gzkb@#)a(;VweoS0jw^&>I&NWL-sxP-rcIUrbvLK+i zBsjM=);HNF#o1+w_YUoV6}wcdRV{T_FW9nZ)uufkFe&*; zM%KN8qSuX0KPFE4KWE;%$y0yNop-jp>f4p8)>hX0j{V!d?~_y9_khe_2^GIf+TKl> zdw$Q+zl!E-HSBljxg9p~J?|9r&_CywU&g1#uASXejx1VpfAii?(xyw4?AB|!?Kbc| zW)^bME%jMI{?`M?ZeO|ayrQ@1%*{)!v->A4pEYCE{B;MnZalnW(YBQywIFp-e8l~uG;l(&+X)Rk0sRM+>^Hw`p3@i8&-Ffwp8a7%J?igEHt z@i6l?bBb~b$O`aF_j8MJ^H28=NDqiBi%YFdO{q-Txpya{EQ64~psba&jJ1TZpT2K~ zr(?Jkk2YgwS>m*1z0;TVHBYMSp50i}RopwfrKmo$x}#|4{w)QKk@{||4q-yJAq?7< z3=>l+)0(w^&TOfiq5k#z7vDKXxyN)C1T0N2aN1KSWFGv!k^K*DbvR3K(@_aYH zI_vPEQ^(F7JjyBZDl31D*Y^#+W*0A-UNJYg#LV^a-0O_B+M5dszH{>QcJ=Wvah;95 zaPv|?;`3*+0%FqQ!h%d9v+m!GnVkCg)zpdeCr3vFOaYp_T7SEFx!>8#PlQ7!h)wrHL*IuU8XIpORioG37s-kWMd^eL-0SNjsr}46+mA0 MboFyt=akR{0B%Aqw*UYD literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/countries/zw.png b/lam/templates/3rdParty/pla/htdocs/images/countries/zw.png new file mode 100644 index 0000000000000000000000000000000000000000..7e69c9e7815cb4c447de09047067031c8bc29dab GIT binary patch literal 1043 zcmeAS@N?(olHy`uVBq!ia0vp^LO{&J!3-oL&dByLFfcO&_=LCux&QzF-zTH6MpDj4 zOZ(A*Lq`kBPdD}KUbkUq$HXO5roTCG?)Q(M=B8%1uHWkE>D|6-&&hM=JG;C0A3L0q zlPc{bY8WS}SI8PY(==+5MfvLRj-6RrxeR*wtd`XRcGVJ|or*cj-6GnoOV$PyO>!)o z&0M>fw`;v@X0!CHefq_H%C$4K+7=u1tubAB+G*hdw~d$l*PRPEa(s93bcP2n!dI8kROkM4$M66BKm80?z24!=uekk(%ieuR zy>VUO$z#P0>lF9w*7)?n^!PEoOBYT4{C0c##OdGPfNx*?R;-xt{{6cPr&v!NU^ucb z{_TtPM|Nl4xm}6&?&d$Dui|e9@$bJb4MOM~(YHDY-wO?CVt zuU=k80s^mme7*z*KKAv!;_3MyFwjX+@k~i^h^eW+iAk!PTbz?qUO+&WpP!+=zMQPA zi<6U~fx*{Lp8^8{0{s2m-P|5Nd{|#!Z*FeR%gYOlVqip%0?ZIN{o1M@D9V`R?e4$8TR4xHwl!tX{P;`<0}`tJkkGvNIDt Z^D(T|H~N_WUh_Q2zn-psF6*2UngBd(Whwvw literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/add.png b/lam/templates/3rdParty/pla/htdocs/images/default/add.png new file mode 100644 index 0000000000000000000000000000000000000000..fe07994c2c79feceb44bb6b69889bf6b09bf4d8b GIT binary patch literal 612 zcmV-q0-ODbP)A4UJtnTRva0zD42_Y-UL^OO!N8utXX$SFiCleN)kXDjF(TUeQpIjAqbE zZ5FwR9e}qa-cnEN%>SC_W#a`6X<+ig_A45pojW1;{(CizT$9+vtTeu`-1x|+^<@^< z-s5wgEL0>E5JdpCW&?|76_Sl~YT?e(twZ&J@$EWG z`4WT0Q}h@5`RRV-JN7}RO@PpZ4D=3CEmj%q9fD0d0?a{xQg?~Ukz<_bIZ5^CFnxu- yuEXCTW!BaG=%#FRuexucX_>CqUtdjklKug3iw=QFq=DrC0000UP)4pXFCHvZDpV>$L9qlRL?K1g)MO(yP2)ywlQecW|6H^0ZFb-8 zo9UseX#L+?2ZmvIGr#X|zB9k?_rd?YiDz8b1gr$!20j2BAP2k(c+U$eP19W4-rl~u zv9a-}ZQB{sH1m7+?)7_mdIHyV1JCpPiHV76$8pX+_mM)O(81>B=HkA6`-1c5&#Pjw zSW!v^T5Iijp6=`G)9G|t&&hG7^;DM_c( z2qCCcDtMk3D5aiCaVVKgZl9Z*JEfGW357x-fMhaxZ$W*1J?-u796EG}SS-f;{5-i_ z4k@Ju@=v|wEnBuM9yxNv)>+adLYZs?apAPQsHv zZLFGF28V{4J=a~me*JpGaU3x+GSXU8Q}bnOYb)2TUDNjfGMP-pb=}^9fq|*%>FIY8 ziGPLf!9+%C*g9k~c)A)Xw3uk)RSQqE?$PhpbSg6($ z_W>vau%(n|PMkOqcO0kAG)<(G4+K_9aqir?#jdWd8{^~SGaVfru|jrMhBQpf&0rcb zU(q@RgsZGPP=La=?H9x0@K7`wz1h^%l>PqLG51O;rElN9t&7E?zI5qQ(9qD30d`3# zx3smjy}RelZ9O~JC-kQJl{zeqf`GSxRX`)K45$Unf0w!i^hFFa*|DZA5?{H3O|NZM zLjwbgH+~*GDq;9`#rJbTx%A=A-TQw{T}f1wVrX^>L&)!Z6?_9^t8^8Cf_Q{}55Cd7 zZi6j^sPoI7p;VnMq;9h8Akf+?DRpO2E6?gothsvSm#AAVQC0z0=d)T0@rC+_s^5V* zARl^2s}YMEHmn=mzWMdd;X<)~axf(qGFgI3B_x%yOheX1q(I9MrJ#b+no6K(jV@DD zh5XCD4i@e!WokM-=+cs)ZH-ze-OY zD2eCrZdnDZ0qTHAHJ}Fs)$ghbP^ykf{Kp@K`zz6?&Se$oZr1>R126Y5vM0>T^Z)<= M07*qoM6N<$f{EdFd;kCd literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/calendar.png b/lam/templates/3rdParty/pla/htdocs/images/default/calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..c3cdf73ce89740cbc988bcf23757347bdef43a08 GIT binary patch literal 478 zcmV<40U`d0P)xp#diF-0gO0wOY#8YPCu(m;08N&*uTGut5+6l*{Fh zwn89;KuU>W7~xpgbw;BRj^m)~dg%B2eO%WKYlIM_(`f)}1&^j_;l$-;pS?|W3xA>5 z;-%N?0T2WMQpztQ+7{CgQKeE*aQMEDWmyW2!C*itl~U^3?KYn0DRqfNVinQywlz(Q z#1ZAKAd*sscsAj%E7u&|-l$%-zys&eh-$T(l7#VijN>>8j`+nH?&XQHNT+m9{OV4r zvu%47(Yhq8!?yNB?Sjbn{m8`F*#eYQL{HfQkBvq&qI$iqBw;ull1wHQY@JRA&+`-< z&1REKCZo(XO>;A%r~|ZX+7v|F`0k?^Fau73<2kaQ(hD$KTsX!7e%mwP_3z)w@3TO; UdX3#eDgXcg07*qoM6N<$f@_Q1TL1t6 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/catalog.png b/lam/templates/3rdParty/pla/htdocs/images/default/catalog.png new file mode 100644 index 0000000000000000000000000000000000000000..907d939ad048efcc4ab31795444bccea409d6ac3 GIT binary patch literal 1302 zcmV+x1?l>UP)4pXFCHvZDpV>$L9qlRL?K1g)MO(yP2)ywlQecW|6H^0ZFb-8 zo9UseX#L+?2ZmvIGr#X|zB9k?_rd?YiDz8b1gr$!20j2BAP2k(c+U$eP19W4-rl~u zv9a-}ZQB{sH1m7+?)7_mdIHyV1JCpPiHV76$8pX+_mM)O(81>B=HkA6`-1c5&#Pjw zSW!v^T5Iijp6=`G)9G|t&&hG7^;DM_c( z2qCCcDtMk3D5aiCaVVKgZl9Z*JEfGW357x-fMhaxZ$W*1J?-u796EG}SS-f;{5-i_ z4k@Ju@=v|wEnBuM9yxNv)>+adLYZs?apAPQsHv zZLFGF28V{4J=a~me*JpGaU3x+GSXU8Q}bnOYb)2TUDNjfGMP-pb=}^9fq|*%>FIY8 ziGPLf!9+%C*g9k~c)A)Xw3uk)RSQqE?$PhpbSg6($ z_W>vau%(n|PMkOqcO0kAG)<(G4+K_9aqir?#jdWd8{^~SGaVfru|jrMhBQpf&0rcb zU(q@RgsZGPP=La=?H9x0@K7`wz1h^%l>PqLG51O;rElN9t&7E?zI5qQ(9qD30d`3# zx3smjy}RelZ9O~JC-kQJl{zeqf`GSxRX`)K45$Unf0w!i^hFFa*|DZA5?{H3O|NZM zLjwbgH+~*GDq;9`#rJbTx%A=A-TQw{T}f1wVrX^>L&)!Z6?_9^t8^8Cf_Q{}55Cd7 zZi6j^sPoI7p;VnMq;9h8Akf+?DRpO2E6?gothsvSm#AAVQC0z0=d)T0@rC+_s^5V* zARl^2s}YMEHmn=mzWMdd;X<)~axf(qGFgI3B_x%yOheX1q(I9MrJ#b+no6K(jV@DD zh5XCD4i@e!WokM-=+cs)ZH-ze-OY zD2eCrZdnDZ0qTHAHJ}Fs)$ghbP^ykf{Kp@K`zz6?&Se$oZr1>R126Y5vM0>T^Z)<= M07*qoM6N<$f{EdFd;kCd literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/children.png b/lam/templates/3rdParty/pla/htdocs/images/default/children.png new file mode 100644 index 0000000000000000000000000000000000000000..6fdc884244c2fc8853003865a8ca6e8e81b5750d GIT binary patch literal 342 zcmV-c0jd6pP)L+c(U z#T3T}XVye(K*0u35u#dv^!o$Z*N<}~Yd}B>ECwtL9|9Yxl)|bATn{1wBJu`=uI6W$ zK|xh*h?dtP`|Ul{H#V`R)?t{zV$iv~SO`YR8?Mh^O1|G;pu?>e?jLTE7decWQo7A- z>wua&N6?R5FdKvvRhnHT(rKfOVf>8DWR+$gWVg93p40Y+_yn}7(B)m6vL0$APx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FZT01FZU(%pXi00007bV*G`2iOb@ z2r(b{**&BH00I_CL_t(I%axI_OB-Pr#(&t0NWnqDrOO2eH*bkkmm-8rUHTt%=n&}Q z;3C*n{{aUV!9he3>aL5+Wyu!O+q60LJQ8Z)YA)vD-S>34NP}rhA9#lM{a$|W!}r3t z)`2>Z*VA_l}bf9Xz_1=`u6s=q9~FvM#dOrS*A2i z)#-G!wzf8cywEFP-Eo|cl}ZKQ_whUr&-0j{pJ#Sfqquk13LNyR9Tilq88dosQBpmGApHIy%zz z^|gk>p@JY#{;^}40+56x00x5r&1RFkyE_&Z7I0mc(aa=G0ga1`i)YB(+#Hr=kt7LG z6g~Gn&jV;of_yEP%L;-(aU84P?<^L=0t9>k>|(L_P%IXEKp%*Jw_||Y+gsh-+&ugI{Core zuz?@dYV~PlW#utX(+6w|z;#`cWcHa1>QPfr(Wwc6w8{d2x) wl0W^%v~v%t)#{UN+fP7`|I`Q)0vYh`7kxx({SNCR3jhEB07*qoM6N<$f_KU=cK`qY literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/country.png b/lam/templates/3rdParty/pla/htdocs/images/default/country.png new file mode 100644 index 0000000000000000000000000000000000000000..7640da3a463dff20a3023b866374eb01475ef018 GIT binary patch literal 707 zcmV;!0zCbRP)UHRJX6Pk=*48fo?kC#-L=iL^ez(?v>jOiywj_z4xe5Y;S`BKo+=rS8 z#xE`caPHw#04f!an`JN>X<$H>8RFQxSF(hxR$*lY!Y~(1b3p-I2l(^k0pN3bJ?Qr# zjy1*@k_4;;6_rrsDl9Mi#L1*EuKb7D1kks=pw_Un0|y6Sj3rIITNMey@C-zCD3`t1 zXjDKBkE*Y(t^zO~dogP*)@GzBgtCNEO9Vj!)LEZ29Qt((dG>L0^fdK&kYTMR2n9-& z34-$=s^FaOI2`)qBT;aeqqkbS(}po|GN8Y|gp}1ORSLU37z|)Cq3ce5{Z9KxTwUZa pN53|;sPfqyJ1>g(M63Tr`~`0e5lx=!z@`8I002ovPDHLkV1n}tIR5|u literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/create.png b/lam/templates/3rdParty/pla/htdocs/images/default/create.png new file mode 100644 index 0000000000000000000000000000000000000000..458c7304b98342f88215e5ccd0f30b12fea2b985 GIT binary patch literal 700 zcmV;t0z>_YP)`C>4?IXf6hSeiXrVT#6+*NTNhgYh zf{ivRYL!OBqYymM7?%X&6%E8S8#eCd-|N5s@3Y9Ek&SOL%{$M`Ff+h^ z=~Li_2d3z#jrSp_cB&-d6$abu!k+IP*TTvkH7Hq<;R8V}6XVVBrVl}SV2sejiYYhu zv{g^+{rNXcV|Ut8OBJiHQ4>cQ2PY^JBmfx_Bmg1^C01eat^J5?xr$_0g&8d)06+kc zx!1lviQMIw;qN9U07;ONKupf1KoS8g0vL%&n0L_s@4mZw2d-r2v2!IsF%%RYFKtMUyGeDJG=m>QPP(u-`qd7pn@`9 zN!5H`Ua5*Od!*-CFlTH&@w)oz)~;)?5k^$!LIAB*c+(6(IR7MdzHWgAZl)W!DEC@{ i?$|PZao_1j^YCwW_X9iEQV`7m0000{XE)7O>#GAj#{7}L9VcR=bMdb&7mhhEH%g7>!R&t^By@nXt2 zQ?txWVxH3~rLM(JON5vhJePi`_2@|Yrn+TOfvtGtEXV65GJWhnF1fG2eYj|ANcX=- zJvB3@WnO$Co!UE1KzqUmRu{GDE#FQoOO3j-Zu=VHHO4FUZWdrtkyd;rb3mg{YgO)? zg})Nt3w+;g`_yHcPFM2kxCf6vmxi4EGV9PrSsr$0me~vp4|-&G%=<0yqr14KZ)&%% ht?H5IF=x{EFor~0O%K}qbSu!a44$rjF6*2Ung9^Ui@X2; literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/delete.gif b/lam/templates/3rdParty/pla/htdocs/images/default/delete.gif new file mode 100644 index 0000000000000000000000000000000000000000..82128aa4ed3a5ec5a077e59b54d51d71688c5558 GIT binary patch literal 624 zcmb7B+e=e%96y>=Na;)~Eh#M zlduX2GUk&n`Zf|QvSlV-M#AVp3WV^I%Feda^n5%034QoHd@g*RKIK=;TAfoxsi-MJ zJpyc}00H1Rz#za&fL8#cG(D=(OaXiX_zdtxN%Z;|fLVY!fO&vvnqCBW zr`3j(%4nR@XeM>K_j>(DgJHsGTm@KDh79u?AgtAfbvhAXgQg|L8Vn*$hZ#mLE|zIp zWSA(+N-Qh0>}q*AmX)E&Bvw>JEf&dQiCV3)&4$(0Vr^}#t_~X-qE2UAA;%>+4xP@3 z%Oxwq?cVfwwmhB)AtE7v2*HjH?Civ@t}ULA@cb^%BhQP15ETST5O8o1eLfr-iuwJL z-;ZNsI6fW^1f)O!gF&2~#f1e7g>Y#JS61Y;HC$iEjSbw~#O-Za6j5O;hLVJd1n%tM z|07>YNlK-g(G`>x~LX%`#L~S(=H*LtKnnoxeZx zQt9`rZnCKTrq0`|x)2BR~HqTCR{GkQc_rWde5> zsps;MrkN4W9PH02ow510xXITCHTNTPCD!4z;|H=I#4W>bD^F&e+m-GWJgh1()4?oZ OnN3PgrY@$YP=>$Dt3g%( literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/device.png b/lam/templates/3rdParty/pla/htdocs/images/default/device.png new file mode 100644 index 0000000000000000000000000000000000000000..eb153a33d13bb21e935456d533388f8baaf5bef9 GIT binary patch literal 500 zcmVel8&MR7pNSL6bdnSz31V6q6H69Zw3N83NEXFiz`u}1 z|AL?!v(cT)F6u7`>8fV$CWO)^x>0MuiKJ!B$F!gnbs)y;B0A3a5eRtTa5;zjp7Wmj zo+~lD31=V;3$9cuc92S?kcUCX<8d2!OF+I)X^`oXt7|nPVL`wz7z?Q+64Us6KBpQ&2Ds||=k+TSfcg2ijE+v)kJd3I zB4I%=D43g@v6~o%K{Oiek8pP0Bb!aTSaeK@blOWKEcmkVk7~6_F82|`I0m3tECMhB zti)pzG+Qc~b|;9c_8lM)c!Q>m^R@5|O?yY7P_PTwvjF0+xha0_mqn+Ycb(2V`FvhD zrT5(EDFJxBA+~ppP?Q9_dj^V<;86cPaBE$g*l7Hr-EO1T6N+@YjJY{101#-I{SF?5i90BrrjbLi>c(*kasZjOX=$m;%=I&cck#@>f z3+dbQ3_r}$)08CBYA}(qEMN+tPB1>!86rv{k`O%%h^G#7bY`CZd++Gz=|e|i7zPT( zXr~1*-bgUm7x4&?SZ%JoL8!gW*A*;(_!;5}g>qX--griHJb}HE!(Pb|>m6WldX9s! zI6}g5NdPQ263o5cQaAzvsSyLh{00006nP)vK$IycYfJkR&X5x@)l&pGte{_}3aK2W>8 z^-1ZE@C)D$unHgs1_t!p+}uNjwOWnMUzT|Py;pcdl{oRi3W@A2p66Z5=ksp^-&%u% zgDfm8U>F95VRXb=Yx4O#+sQQ^e>BO`*()Rljxj$!Pdc4GSg+U57K_DKjr#>!YoaJ3 z3_~iF3gxx0$W1*-^VbXHR~?SNJcDIfEG{l0gvh2+sgkuP28g1lbKh9|nR}IW_Pe7L zu2%430qA zl8K24W@e6)$z&b`>^UeD3cU4vliozgm&GfP9%XiR7Kji+6NVCKIsKP|-Q8U-FMrAt zW6$yD#&=v;33=z;lQbG0zVB17*YSNH&+~|)h)xZZk|?EEUjCZVK|wmM`D|sKzJoc2 zhKA5u69f_|n*@QR(eTk)w*Y&um&;|;_9c$J_zcz6bA0og!_ng};W#!sJ3BO+4T3-d z0^e_R0a`}{LBPhD3$eWy7y1|)C2IJ$;QmItf+PaD7d03W3GMU76UE=XLrfGDGC}qI!H*WFcb%V?6 zRdoCS+3YY1)y6oGR zYzu9K&`2pe!v+rknoLbia`^BV{r&y)_4VPp32fWJvP^^!9Z?j8fJTTm0Kl>wPMtbQ z7)F?;XfKxj$DXu@#}FtLbtCwGgKD+fwx;j1_;7$}n$&7Flv0SEo*q3iGJ@}W|M{V` ic-`DWi1yUpKJhmX37afwuiEDT0000IS@C4*({TfjOl$aNYd)qT}6#gN(1PO_$Zg=Ot-Ms_;raJ(2T_;(`D9WPj zrfoJ)n=sW5hXa73C~Rk0c6@&WuwJhv0SJ?+D2o2Decm2Fj#HTDD*)N`C=|~_iNE-m z_%!%pjMs(aS_VM305^C{0BQKcy96Y7dU)rrssiv?RR9(bi@toSPXIy)qINI4p>NZ+ z`6^#7A0r~orfX;Sv##sPvW$ph-(HkNQR+leR9Tku`Mk%6eo|4kNZFQUiq`9X?{TNl z?{O{L=i3ATq$Y+$%ZX{^iSj^^lzh*HkFrVGH%(`KWywIqKlKg!$p=c0nGEv)0000< KMNUMnLSTX}zm|0X literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/error-big.png b/lam/templates/3rdParty/pla/htdocs/images/default/error-big.png new file mode 100644 index 0000000000000000000000000000000000000000..0539ce8304f1b20c076c135fc6c629e5c252694f GIT binary patch literal 1891 zcmV-p2b}ncP)`mq4rpp!dqSx$dnJ3{CjdFnNueBpetYiU5NlABiwYzw=nPUXR`Y+%=p zO@Kh8XF!h;oe6W|#0mcJ`~;6abMdM`R|0Gcd+_dF9@sYk=`bStHX?BWGEu-l2*Jg0 znn+Z~5Q4jZG|HnQaQsSuEpz?NP#s-zGX-bFOpgfMjBF=Oy_HJ&`otNBC}~m#uK^bTY%pIwlM=) z4;GRo!5?|(^Q`aD5Hq7-q)ax+--TSgf^UBRFzU76K<)k(`+oX2+Y2|>Iaw5Ra{H$n zXbvd(X4_b~tYRMiSMJWHH6eby|0cxraCWLqv<~F@>)H2%CpdIuaDI63WQ0gG!*$Cv zd>-?yYW)gANUUtvc>3sM{=FXrzGnwj$it>eq^c^vKXeyj_N--!0Z{zl(D>OC$1J9w z|8as?(jc8NN0%f?bcEb2Yw@uAvki6(?d1Yn#vc0m76P6)Vs_N-8c3c+AQgVW6Dfmq z##)V4MW$o1haH>C4-GqDJ$I+Kr;YpXSciz5EC__d&AIV&1HX z_bjzDTfCePY}!{8h5dVOzzDx)joV!5NkVn@MFXHpbKq18&p>;#n~!z+_;{zKb+&jp zAK2vGRb4H7VPgoA?-hoflMsa)FrV9jEEfh=6`6)wl>=XDwKJQ%Tr91z|DGEWGpCA^ z0_brN2A}Auv6xn0>%;AoQ60sLSY4GuPrFvq%ela&yt|}XWBcZ7Ao+etUXcoeNU?TB zz+zj|?Z@YJ;3xqsNfItq;^07=oq7H#a)C|F&F))!5%KdBYG_Q#M8HUK>!u|Z)0WjW zxSd5O5`a|NpuR@snvloN%kwo{&ikF$p4)H8=G}56bVO{NwSC=$>Yek$EkP$u0Vg%S zQXK#|RT)*0(cCfvTbl|VcNugbcW}e1Is#e-blcw5Udi_msWA6_@q>5dwoe5y1Z8;` zvM;XR>tIE@W@q*ST+Y^}aJX}OFC=X$S}DcGxPN<)e-77?N(**wZlELNMk-QNawSO; zey>7zo0pr{*YcNRrXDCtPI)rrr1LNCC8+)vlJ8aqtoT845KCmZG&M`WtD|{JheN&$ zL-3ntFYvWPXYx&wOkVtk>RmJh1CX*bB*mrHAXW7s2RGnbvW>Q$t!O@f#jpUfEU|5! zHRCV`>&_k6xVmL7O=WCPC?v_(!O5`{pWOK{YUh32c-v3-`|B5oC3Fm<@~?WuA@RBX z+WcX!0P9#qgdmng`m*2?d?PLFr@SVrcGCCe-YPPb6hitGm2b`8+ znY{0))=)#%!<7*ul9RlA+#K_-oSG$?Fv#c&TQ}+J^5qZhw0QW~&>73uL`fM*MEpGc zE6t^OZI7R1+Mrsqw2mbeO&Cl^_59&qTYz62d>bLksBR_+NKEj{Z?B_&RW|PWx_k_L zrh&#<6<3*g__E?jo#8W4-uTaKzUeyKwD#;H*R#GWgXXU6Mo20t;bG$4Nv0wRYWxbJ zpsQpbk*lfM4F5P0VQMDBfnS|vV$u}=77S@{Q-7tYGiSPi(s{a$0e6txk@Rc3ocEk znViXRaWci=$r=9i()%1gU2xUPbmy-EzbJHMU#FLC>jHGQX{=vah1)5Y>=A?zWOPA1 zX&?j`0+MNC9`>zsafVMvdFl8xFAPnWdOiiv^IcUGI#<0NUg~R9RxH(M2&fnaEMKD0 z+UUYDSB(t=R7K+XSHkFqU?!$>^v!9Us?2kPMLxN#asdTXm&%ndR$r~{%Y4wFm6Qw| z{E$GmlJ)9>Ult#*<^Kry6xnsPnmI>Fg;W-0;x$v zK~#90jgw1Alu;DNfA{-7XPWVwhvQ?6nL$&>L7RzulvG4%MMj$fiNbcaX_Jvf2-T)V zE!qS@^jMTZ5kZ@vsEou-4=R*i20>sjVx#G3!%Uy^&22GGo0fGxyc0?Ba4=jVm*y+)6i`y zfl>=%zN&6h*}r2cx=q!9BY;qVl|aLuZ9$4{v%gI$sqv}oAxP1uQUQ^{E(C8SM>X-ZOa8J@kH<<{c~9=)7qd}-VNy+y-QM-l!00006{{8>=@8ADv%Z~mJ$)5fH z#KlKoK03a7@9qEM+79gQtT&gPo-|gSM&^Rs(+g_`&ex z;bVs1zkmG&@c{w|V!$_$KYuYi`}K>##zYlp`(K9F@4qrUd;JH@{s(l;?|=UozI^`5 zaPstZhEJb9F#x>-HV_~HhjWYrAP59e^xyxM6jX-An2oTbA+8jY-@VKR+y|kTQqRQ1 zn87{&D&gRA$BvFQLm{5rTKfQ>0D>3*4E4`IL;eFH9}hbND=Q-dGb01Tf1r!L|NF(j z`12 z@&hRPpJCzZJq*__U4a__5J1pCgv9aBAAcEMz5EGu5zuB}lzazi28x3W`14!<#qn7=*akf!_HB=70YV(F8N-`}gk*OpHuG{#Rf?ego=)!~#G7L1WJ0tjNjr!TA@UcO;?$Gsl>91cufEcLhKO@73*RL7=eg6d1#mEZM3lKnzpuphg_qfNz_+N;D z@izlEHwQS8{Qdh6Yzrt!gVewh&eyLWfvNB_kj45An36{{8>=@8ADv%Z~mJ$)5fH z#KlKoK03a7@9qEM+79gQtT&gPo-|gSM&^Rs(+g_`&ex z;bVs1zkmG&@c{w|V!$_$KYuYi`}K>##zYlp`(K9F@4qrUd;JH@{s(l;?|=UozI^`5 zaPstZhEJb9F#x>-HV_~HhjWYrAP59e^xyxM6jX-An2oTbA+8jY-@VKR+y|kTQqRQ1 zn87{&D&gRA$BvFQLm{5rTKfQ>0D>3*4E4`IL;eFH9}hbND=Q-dGb01Tf1r!L|NF(j z`12 z@&hRPpJCzZJq*__U4a__5J1pCgv9aBAAcEMz5EGu5zuB}lzazi28x3W`14!<#qn7=*akf!_HB=70YV(F8N-`}gk*OpHuG{#Rf?ego=)!~#G7L1WJ0tjNjr!TA@UcO;?$Gsl>91cufEcLhKO@73*RL7=eg6d1#mEZM3lKnzpuphg_qfNz_+N;D z@izlEHwQS8{Qdh6Yzrt!gVewh&eyLWfvNB_kj45An3Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOb@ z2r)GyV)SwV00a$5L_t(Y$BmY|ZxvS%#(#5m_xgbe;t_}-N|Pdhgk903w=gB7Ar%D; z^eOWPP*KqZ6e^wtL9vTaIZBr}jfH{{mJ==}R<0BKCYHJTI5U&t?m7nBI7eFT-m_=t z`{w)RoVmgdLqrH6u-kBV0L)AP-xB8?-wpuTF6*0sJrPlN-vY7)AcTOK4UCPA&7D1a z_Gq5xRS02QB2`t9Wtl&F_Uz}Wsi`l386W}Tj>;rSk^vuFx^!vX-Fup>`QMkL?%F2o^EiL(l3!ncAjEl&g&6&$K%%4t7OdN|bCRvt+otP&{!hr+(Idy83 zg@py3ocuPOp8o!aqA0EaZD1X+bd#wWu>bVw(?_mez1mt^Tk|wcr7^NGQ$!dZ9;Vyv z(&=>k&Ye4Q{P-u~;>C+Re?I@w!otGGBC_i41yG8J;O+w_PoA7RbLPy^s;ZJK%Yul! zk)eoSW(*DvvTxr$Mn^|!x7*yh^|c>6_MzXn@l`l??);1C>FF<%B>7QQ7lGGl4UmqH zj~~8v?OLm9Z#M-;rjLK%*^~mQIy;6o5llaO0iia0`C7wBjS{0 z#mLBBCMG5r8QH_+

'; + echo ''; + echo ''; + printf('',IMGDIR,'error-big.png'); + + printf('',$type['description'],_('Parse Error')); + echo ''; + + printf('',_('Description'),$request->error['message']); + printf('',_('Line'),$request->error['line']); + printf('',_('Data')); + + foreach ($request->error['data'] as $line) + printf('',$line); + + echo '

%s %s

%s:%s
%s:%s
%s:
 %s
'; + echo '
'; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/import_form.php b/lam/templates/3rdParty/pla/htdocs/import_form.php new file mode 100644 index 00000000..9c6bbbb6 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/import_form.php @@ -0,0 +1,48 @@ +getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->drawTitle(sprintf('%s',_('Import'))); +$request['page']->drawSubTitle(sprintf('%s: %s',_('Server'),$app['server']->getName())); + +echo '
'; +echo '
'; +printf('',$app['server']->getIndex()); +echo ''; +echo '
'; + +echo ''; + +echo ''; +echo ''; +printf('',_('Select an LDIF file')); +echo ''; + +printf('',_('Maximum file size'),ini_get('upload_max_filesize')); + +echo ''; +printf('',_('Or paste your LDIF here')); +echo ''; +echo ''; +printf('', + _("Don't stop on errors")); +printf('',_('Proceed >>')); +echo '
 
%s'; +echo ''; +echo '
 %s %s
 
%s
 
 %s
 
'; +echo '
'; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/index.php b/lam/templates/3rdParty/pla/htdocs/index.php new file mode 100644 index 00000000..d2113b7c --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/index.php @@ -0,0 +1,147 @@ + + +If you are seeing this in your browser, +PHP is not installed on your web server!!! + + +*******************************************/ + +/** + * We will perform some sanity checking here, since this file is normally loaded first when users + * first access the application. + */ + +# The index we will store our config in $_SESSION +define('APPCONFIG','plaConfig'); + +define('LIBDIR',sprintf('%s/',realpath('../lib/'))); +ini_set('display_errors',1); +error_reporting(E_ALL); + +# General functions needed to proceed. +ob_start(); +if (! file_exists(LIBDIR.'functions.php')) { + if (ob_get_level()) ob_end_clean(); + die(sprintf("Fatal error: Required file '%sfunctions.php' does not exist.",LIBDIR)); +} + +if (! is_readable(LIBDIR.'functions.php')) { + if (ob_get_level()) ob_end_clean(); + die(sprintf("Cannot read the file '%sfunctions.php' its permissions may be too strict.",LIBDIR)); +} + +if (ob_get_level()) + ob_end_clean(); + +# Make sure this PHP install has pcre +if (! extension_loaded('pcre')) + die('

Your install of PHP appears to be missing PCRE support.

Please install PCRE support before using phpLDAPadmin.
(Dont forget to restart your web server afterwards)

'); + +require LIBDIR.'functions.php'; + +# Define the path to our configuration file. +if (defined('CONFDIR')) + $app['config_file'] = CONFDIR.'config.php'; +else + $app['config_file'] = 'config.php'; + +# Make sure this PHP install has session support +if (! extension_loaded('session')) + error('

Your install of PHP appears to be missing php-session support.

Please install php-session support before using phpLDAPadmin.
(Dont forget to restart your web server afterwards)

','error',null,true); + +# Make sure this PHP install has gettext, we use it for language translation +if (! extension_loaded('gettext')) + system_message(array( + 'title'=>_('Missing required extension'), + 'body'=>'Your install of PHP appears to be missing GETTEXT support.

GETTEXT is used for language translation.

Please install GETTEXT support before using phpLDAPadmin.
(Dont forget to restart your web server afterwards)', + 'type'=>'error')); + +# Make sure this PHP install has all our required extensions +if (! extension_loaded('ldap')) + system_message(array( + 'title'=>_('Missing required extension'), + 'body'=>'Your install of PHP appears to be missing LDAP support.

Please install LDAP support before using phpLDAPadmin.
(Dont forget to restart your web server afterwards)', + 'type'=>'error')); + +# Make sure that we have php-xml loaded. +if (! function_exists('xml_parser_create')) + system_message(array( + 'title'=>_('Missing required extension'), + 'body'=>'Your install of PHP appears to be missing XML support.

Please install XML support before using phpLDAPadmin.
(Dont forget to restart your web server afterwards)', + 'type'=>'error')); + +/** + * Helper functions. + * Our required helper functions are defined in functions.php + */ +if (isset($app['function_files']) && is_array($app['function_files'])) + foreach ($app['function_files'] as $file_name ) { + if (! file_exists($file_name)) + error(sprintf('Fatal error: Required file "%s" does not exist.',$file_name),'error',null,true); + + if (! is_readable($file_name)) + error(sprintf('Fatal error: Cannot read the file "%s", its permissions may be too strict.',$file_name),'error',null,true); + + ob_start(); + require $file_name; + if (ob_get_level()) ob_end_clean(); + } + +# Configuration File check +if (! file_exists($app['config_file'])) { + error(sprintf(_('You need to configure %s. Edit the file "%s" to do so. An example config file is provided in "%s.example".'),app_name(),$app['config_file'],$app['config_file']),'error',null,true); + +} elseif (! is_readable($app['config_file'])) { + error(sprintf('Fatal error: Cannot read your configuration file "%s", its permissions may be too strict.',$app['config_file']),'error',null,true); +} + +# If our config file fails the sanity check, then stop now. +if (! $config = check_config($app['config_file'])) { + $www['page'] = new page(); + $www['body'] = new block(); + $www['page']->block_add('body',$www['body']); + $www['page']->display(); + exit; + +} else { + app_session_start(); + $_SESSION[APPCONFIG] = $config; +} + +if ($uri = get_request('URI','GET')) + header(sprintf('Location: cmd.php?%s',base64_decode($uri))); + +if (! preg_match('/^([0-9]+\.?)+/',app_version())) { + system_message(array( + 'title'=>_('This is a development version of phpLDAPadmin'), + 'body'=>'This is a development version of phpLDAPadmin! You should NOT use it in a production environment (although we dont think it should do any damage).', + 'type'=>'info','special'=>true)); + + if (count($_SESSION[APPCONFIG]->untested())) + system_message(array( + 'title'=>'Untested configuration paramaters', + 'body'=>sprintf('The following parameters have not been tested. If you have configured these parameters, and they are working as expected, please let the developers know, so that they can be removed from this message.
%s',implode(', ',$_SESSION[APPCONFIG]->untested())), + 'type'=>'info','special'=>true)); + + $server = $_SESSION[APPCONFIG]->getServer(get_request('server_id','REQUEST')); + if (count($server->untested())) + system_message(array( + 'title'=>'Untested server configuration paramaters', + 'body'=>sprintf('The following parameters have not been tested. If you have configured these parameters, and they are working as expected, please let the developers know, so that they can be removed from this message.
%s',implode(', ',$server->untested())), + 'type'=>'info','special'=>true)); +} + +include './cmd.php'; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/mass_delete.php b/lam/templates/3rdParty/pla/htdocs/mass_delete.php new file mode 100644 index 00000000..b2209f32 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/mass_delete.php @@ -0,0 +1,147 @@ +_('No entry selected'), + 'body'=>_('No entry was selected to delete'), + 'type'=>'warn'),'index.php'); + +if (! is_array($request['dn'])) + $request['dn'] = array($request['dn']); + +$request['children'] = array(); +$request['parent'] = array(); +foreach ($request['dn'] as $dn) { + # Check if the entry exists. + if (! $dn || ! $app['server']->dnExists($dn)) + system_message(array( + 'title'=>_('Entry does not exist'), + 'body'=>sprintf('%s (%s/%s)',_('The entry does not exist and will be ignored'),$dn), + 'type'=>'error')); + + # We search all children, not only the visible children in the tree + if (! in_array_ignore_case($dn,$request['children'])) { + $request['children'] = array_merge($request['children'],$app['server']->getContainerContents($dn,null,0,'(objectClass=*)',LDAP_DEREF_NEVER)); + array_push($request['parent'],$dn); + } +} + +printf('

%s

',_('Mass Delete')); +printf('

%s: %s

',_('Server'),$app['server']->getName()); +echo "\n"; + +echo '
'; +echo ''; + +if (count($request['parent']) == 1) + printf('',_('Are you sure you want to permanently delete this object?')); +else + printf('',_('Are you sure you want to permanently delete these objects?')); + +echo ''; +printf('',_('Server'),$app['server']->getName()); + +foreach ($request['parent'] as $dn) + printf('', + _('DN'),_('DN'),$dn); + +echo ''; + +$request['delete'] = $request['parent']; + +if (count($request['children'])) { + printf('',_('Permanently delete all children also?')); + echo ''; + + # We need to see if the children have children + $query = array(); + $query['scope'] = 'sub'; + $query['attrs'] = array('dn'); + $query['size_limit'] = 0; + $query['deref'] = LDAP_DEREF_NEVER; + + $request['search'] = array(); + foreach ($request['children'] as $dn) { + $query['base'] = $dn; + $request['search'] = array_merge($request['search'],$app['server']->query($query,null)); + } + + foreach ($request['search'] as $value) + array_push($request['delete'],$value['dn']); + + echo ''; + echo ''; + + printf('', + sprintf(_('phpLDAPadmin can also recursively delete all %s of the child entries. See below for a list of all the entries that this action will delete. Do you want to do this?'),count($request['children']))); + + echo ''; + + printf('', + _('Note: this is potentially very dangerous and you do this at your own risk. This operation cannot be undone. Take into consideration aliases, referrals, and other things that may cause problems.')); + echo "\n"; + + echo ''; + + echo "\n"; + + printf('',_('List of entries to be deleted:')); + echo ''; + + $i = 0; + echo ''; + echo "\n"; + + echo ''; +} + +echo ''; +echo ''; + +echo ''; +echo ''; +echo "\n"; + +echo '
%s
%s
 
%s:%s
%s%s
 
%s
 
'; + printf(_('This request also includes %s children entries.'),count($request['children'])); + echo '
%s
 
%s
 
%s
 
'; + printf(''; + echo '
 
'; +echo '
'; +echo ''; +printf('',$app['server']->getIndex()); +foreach ($request['parent'] as $dn) + printf('',htmlspecialchars($dn)); +printf('',sprintf(_('Delete all %s objects'),count($request['delete']))); +echo '
'; +echo '
'; + +echo '
'; +echo ''; +printf('',$app['server']->getIndex()); +printf('',_('Cancel')); +echo '
'; + +echo '
'; +echo '
'; + +echo '
'; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/mass_edit.php b/lam/templates/3rdParty/pla/htdocs/mass_edit.php new file mode 100644 index 00000000..1ea70013 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/mass_edit.php @@ -0,0 +1,133 @@ +dnExists($dn)) { + system_message(array( + 'title'=>_('Entry does not exist'), + 'body'=>sprintf('%s (%s/%s)',_('The entry does not exist and will be ignored'),$dn), + 'type'=>'error')); + + continue; + } + + $request['page'][$counter] = new MassRender($app['server']->getIndex(),'none'); + $request['page'][$counter]->setDN($dn); + $request['page'][$counter]->accept(true); + + $template = $request['page'][$counter]->getTemplate(); + + # Mark our attributes to edit as shown. + foreach ($template->getAttributes(true) as $attribute) { + if ($attribute->isInternal()) + continue; + + if (in_array_ignore_case($attribute->getName(),$request['attrs']) || in_array('*',$request['attrs'])) { + $attribute->show(); + + # Get a list of our columns (we are not interested in these attribute values) + if (! isset($attrcols[$attribute->getName()])) + $attrcols[$attribute->getName()] = $attribute; + } + } + + $counter++; +} + +usort($attrcols,'sortAttrs'); + +if (! count($request['page'])) + header('Location: index.php'); + +# We'll render this forms Title with the first DN's object. +$request['page'][0]->drawTitle(_('Bulk edit the following DNs')); +$request['page'][0]->drawSubTitle(sprintf('%s: %s',_('Server'),$app['server']->getName())); + +echo '
'; +echo '
'; +echo ''; +printf('',$app['server']->getIndex()); + +foreach ($request['page'] as $j => $page) + printf('',$j,$page->getTemplate()->getDN()); + +echo '
'; + +echo ''; +echo ''; +echo ''; + +foreach ($attrcols as $attribute) { + echo ''; +} + +echo ''; + +$counter = 0; +foreach ($request['page'] as $j => $page) { + $template = $page->getTemplate(); + + printf('',$counter++%2==0?'even':'odd'); + printf('', + $template->getDN(),substr($template->getDN(),0,20)); + + foreach ($attrcols as $attrcol) { + $attribute = $template->getAttribute($attrcol->getName()); + + echo ''; + } + + echo ''; +} + +echo '
DN'; + $request['page'][0]->draw('Name',$attribute); + echo '
%s...'; + if ($attribute) { + foreach ($attribute->getValues() as $i => $val) + $page->draw('MassFormReadWriteValue',$attribute,$i,$j); + + # The attribute doesnt exist. If it is available by the shema, we can draw an empty input box. + } else { + $match = false; + + foreach ($template->getAvailAttrs() as $attribute) { + if ($attrcol->getName() == $attribute->getName()) { + $page->draw('MassFormReadWriteValue',$attribute,0,$j); + $match = true; + + break; + } + } + + if (! $match) + printf('
%s
', _('Attribute not available')); + } + + echo '
'; +echo '
'; +echo '
'; +printf('',_('Update Values')); +echo '
'; +echo '
'; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/mass_update.php b/lam/templates/3rdParty/pla/htdocs/mass_update.php new file mode 100644 index 00000000..8a9dd01e --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/mass_update.php @@ -0,0 +1,175 @@ + $dn) { + # Check if the entry exists. + if (! $dn || ! $app['server']->dnExists($dn)) { + system_message(array( + 'title'=>_('Entry does not exist'), + 'body'=>sprintf('%s (%s/%s)',_('The entry does not exist and will be ignored'),$dn), + 'type'=>'error')); + + continue; + } + + # Simulate the requirements for *Render->accept() + if (! isset($request['mass_values'][$index])) + continue; + + $_REQUEST['new_values'] = $request['mass_values'][$index]; + + $render = new MassRender($app['server']->getIndex(),'none'); + $render->setDN($dn); + $render->accept(true); + + if ($render->getTemplate()->getLDAPmodify(false,$index)) + $request['update'][$index] = $render; +} + +# We can use the $render to give us a title +$render->drawTitle(_('Bulk update the following DNs')); +$render->drawSubTitle(sprintf('%s: %s',_('Server'),$app['server']->getName())); + +if (count($request['update'])) { + if (get_request('confirm','REQUEST')) { + foreach ($request['update'] as $index => $page) { + $template = $page->getTemplate(); + + # Perform the modification + $result = $app['server']->modify($template->getDN(),$template->getLDAPmodify(false,$index)); + + if ($result) + printf('%s: %s
',$template->getDN(),_('Modification successful!')); + else + printf('%s: %s
',$template->getDN(),_('Modification NOT successful!')); + } + + } else { + echo '
'; + echo ''; + printf('',$app['server']->getIndex()); + echo ''; + + foreach ($request['update'] as $j => $page) + printf('',$j,$page->getTemplate()->getDN()); + + echo ''; + echo ''; + echo '
'; + + echo '
'; + + echo ''; + echo ''; + echo '
'; + printf(_('There will be %s updates done with this mass update'),sprintf('%s',count($request['update']))); + echo '
'; + + echo '
'; + + foreach ($request['update'] as $index => $page) { + $template = $page->getTemplate(); + + echo ''; + echo ''; + printf('',IMGDIR,get_icon($app['server']->getIndex(),$template->getDN())); + + printf('', + $app['server']->getIndex(),rawurlencode(dn_unescape($template->getDN())),htmlspecialchars(get_rdn($template->getDN()))); + echo ''; + + printf('', + htmlspecialchars(dn_unescape($template->getDN())),_('Old Value')); + + foreach ($template->getLDAPmodify(true,$index) as $attribute) { + echo ''; + echo ''; + + echo ''; + + # Show NEW Values + echo ''; + + # Show OLD Values + echo ''; + + echo ''; + } + + echo '
icon%s
 dn%s%s
 '; + $page->draw('Name',$attribute); + echo ''; + + if (! $attribute->getValueCount() || $attribute->isForceDelete()) { + printf('[%s]',_('attribute deleted')); + printf('',$index,$attribute->getName(),0,''); + } + + foreach ($attribute->getValues() as $key => $value) { + # For multiple values, we'll highlight the changed ones + if ((count($attribute->getValues()) > 5) && in_array($value,$attribute->getAddedValues())) + echo ''; + + $page->draw('CurrentValue',$attribute,$key); + + # For multiple values, close the highlighting + if ((count($attribute->getValues()) > 5) && in_array($value,$attribute->getAddedValues())) + echo ''; + + echo '
'; + printf('',$index,$attribute->getName(),$key,$value); + } + + echo '
'; + + if (! $attribute->getOldValues()) + printf('[%s]',_('attribute doesnt exist')); + + foreach ($attribute->getOldValues() as $key => $value) { + # For multiple values, we'll highlight the changed ones + if ((count($attribute->getOldValues()) > 5) && in_array($value,$attribute->getRemovedValues()) && count($attribute->getValues())) + echo ''; + + $page->draw('OldValue',$attribute,$key); + + # For multiple values, close the highlighting + if ((count($attribute->getOldValues()) > 5) && in_array($value,$attribute->getRemovedValues()) && count($attribute->getValues())) + echo ''; + + echo '
'; + } + + echo '
'; + + echo '
'; + } + + echo '
'; + printf('',_('Update Values')); + echo '
'; + } + +} else { + echo '
'; + echo _('You made no changes'); + echo '
'; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/modify_member_form.php b/lam/templates/3rdParty/pla/htdocs/modify_member_form.php new file mode 100644 index 00000000..208b9efe --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/modify_member_form.php @@ -0,0 +1,178 @@ +getIndex(),get_request('template','REQUEST',false,null)); +$request['page']->setDN($request['dn']); +$request['page']->accept(true); +$request['template'] = $request['page']->getTemplate(); + +if (! is_null($request['dn'])) + $rdn = get_rdn($request['dn']); +else + $rdn = null; + +# Get all current group members +$current_members = $app['server']->getDNAttrValue($request['dn'],$request['attr']); +usort($current_members,'pla_compare_dns'); + +# Loop through all base dn's and search possible member entries +$query = array(); + +# Get all entries that can be added to the group +if (preg_match("/^".$request['attr']."$/i",$_SESSION[APPCONFIG]->getValue('modify_member','posixgroupattr'))) { + $query['filter'] = $_SESSION[APPCONFIG]->getValue('modify_member','posixfilter'); + $attr = $_SESSION[APPCONFIG]->getValue('modify_member','posixattr'); + +} else { + $query['filter'] = $_SESSION[APPCONFIG]->getValue('modify_member','filter'); + $attr = $_SESSION[APPCONFIG]->getValue('modify_member','attr'); +} + +$query['attrs'] = array($attr); + +$possible_values = array(); +foreach ($app['server']->getBaseDN() as $base) { + $query['base'] = $base; + + $possible_values = array_merge($possible_values,$app['server']->query($query,null)); +} + +usort($possible_values,'pla_compare_dns'); + +$request['page']->drawTitle(sprintf('%s %s',_('Modify group'),get_rdn($request['dn']))); +$request['page']->drawSubTitle(); + +printf('%s %s %s %s:', + _('There are'),count($current_members),_('members in group'),htmlspecialchars(get_rdn($request['dn']))); + +$possible_members = array(); +for ($i=0;$igetValue('modify_member','posixgroupattr'))) + $possible_members[$i] = $possible_values[$i][$_SESSION[APPCONFIG]->getValue('modify_member','posixattr')][0]; + else + $possible_members[$i] = $possible_values[$i][$_SESSION[APPCONFIG]->getValue('modify_member','attr')]; +} + +# Show only user that are not already in group. +$possible_members = array_diff($possible_members,$current_members); +usort($possible_members,'pla_compare_dns'); + +/* Draw form with select boxes, left for all possible members and + * right one for those that belong to group */ + +# Modifications will be sent to update_confirm which takes care of rest of the processing +echo '
'; +echo '
'; + +echo '
'; +echo '
'; +if ($_SESSION[APPCONFIG]->getValue('confirm','update')) + echo ''; +else + echo ''; +echo '
'; + +echo ''; + +echo ''; +printf('',IMGDIR,_('Available members')); +printf('',IMGDIR,_('Group members')); +echo ''; + +# Generate select box from all possible members +echo ''; +echo ''; + +# Generate select box from all current members +echo ''; + +echo ''; + +# Show buttons which move users from left to right and vice versa +echo ''; +echo ''; +echo ''; +echo ''; + +echo ''; + +echo '
Users %sMembers %s
'; +echo ''; +echo ''; +echo ''; +echo '
'; +printf(' ', + _('Add selected'),_('Add all')); +echo ''; +printf(' ', + _('Remove selected'),('Remove all')); +echo '
'; + +# Hidden attributes for update_confirm.php +printf('',$app['server']->getIndex()); +printf('',rawurlencode($request['dn'])); +printf('',$request['attr']); + +/* Generate array of input text boxes from current members. + * update_confirm.php will see this as old_values[member-attribute][item] */ +for ($i=0; $i', + htmlspecialchars($request['attr']),$i,htmlspecialchars($current_members[$i])); + +/* Javascript generates array of input text boxes from new members. + * update_confirm.php will see this as new_values[member-attribute][item] + * Input text boxes will be generated to div=dnu */ +echo '
'; +printf('',htmlspecialchars($request['attr'])); +echo '
'; + +# Submit values to update_confirm.php and when clicked, run addSelected +printf('',_('Save changes'),$request['attr']); +echo '
'; +echo '
'; +printf('',JSDIR); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/password_checker.php b/lam/templates/3rdParty/pla/htdocs/password_checker.php new file mode 100644 index 00000000..c616e2e7 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/password_checker.php @@ -0,0 +1,88 @@ +%s',_('Password Checker Tool')); + +echo '
'; +echo ''; +printf('',$request['attribute']); + +echo ''; + +echo ''; +printf('',_('Compare')); +printf('', + (obfuscate_password_display($request['enc_type']) ? 'password' : 'text'),htmlspecialchars($request['hash'])); +echo ''; + +echo ''; +printf('',_('To')); +printf('', + htmlspecialchars($request['password'])); +echo ''; + +echo ''; +echo ''; + +echo ''; +echo ''; +echo '
%s
%s
 '; + +if ($request['action'] == 'compare') { + echo '    '; + + if (password_check($request['hash'],$request['password'],$request['attribute'])) + printf('%s',_('Passwords match!')); + else + printf('%s',_('Passwords do not match!')); + + echo ''; +} + +echo '
'; +echo '
'; + +# Pull our password from the form that opened this window. +if ($request['componentid']) { + echo ''; +} + +# Capture the output and put into the body of the page. +$www['body'] = new block(); +$www['body']->SetBody(ob_get_contents()); +$www['page']->block_add('body',$www['body']); +ob_end_clean(); + +# Render the popup. +$www['page']->display(array('CONTROL'=>false,'FOOT'=>false,'HEAD'=>false,'TREE'=>false)); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/query_engine.php b/lam/templates/3rdParty/pla/htdocs/query_engine.php new file mode 100644 index 00000000..a94feddd --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/query_engine.php @@ -0,0 +1,19 @@ +getIndex(),get_request('query','REQUEST',false,null)); +$request['page']->accept(); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/rdelete.php b/lam/templates/3rdParty/pla/htdocs/rdelete.php new file mode 100644 index 00000000..bb864a9d --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/rdelete.php @@ -0,0 +1,89 @@ +dnExists($dn)) + system_message(array( + 'title'=>_('Entry does not exist'), + 'body'=>sprintf('%s (%s)',_('Unable to delete entry, it does not exist'),$dn), + 'type'=>'error')); + else + array_push($request['parent'],$dn); + +printf('

%s

',_('Delete LDAP entries')); +printf('

%s

',_('Recursive delete progress')); + +# Prevent script from bailing early on a long delete +@set_time_limit(0); + +foreach ($request['parent'] as $dn) { + echo '

'; + echo ''; + $result = pla_rdelete($app['server'],$dn); + echo '
'; + + if ($result) { + printf(_('Entry %s and sub-tree deleted successfully.'),''.$dn.''); + + } else { + system_message(array( + 'title'=>_('Could not delete the entry.').sprintf(' (%s)',pretty_print_dn($request['dn'])), + 'body'=>ldap_error_msg($app['server']->getErrorMessage(null),$app['server']->getErrorNum(null)), + 'type'=>'error')); + } +} + +function pla_rdelete($server,$dn) { + # We delete all children, not only the visible children in the tree + $children = $server->getContainerContents($dn,null,0,'(objectClass=*)',LDAP_DEREF_NEVER); + + if (! is_array($children) || count($children) == 0) { + printf('%s %s...',_('Deleting'),$dn); + + if ($server->delete($dn)) { + printf(' %s
',_('Success')); + return true; + + } else { + system_message(array( + 'title'=>_('Could not delete the entry.').sprintf(' (%s)',pretty_print_dn($dn)), + 'body'=>ldap_error_msg($server->getErrorMessage(null),$server->getErrorNum(null)), + 'type'=>'error')); + } + + } else { + foreach ($children as $child_dn) + pla_rdelete($server,$child_dn); + + printf('%s %s...',_('Deleting'),$dn); + + if ($server->delete($dn)) { + printf(' %s
',_('Success')); + return true; + + } else { + system_message(array( + 'title'=>_('Could not delete the entry.').sprintf(' (%s)',pretty_print_dn($dn)), + 'body'=>ldap_error_msg($server->getErrorMessage(null),$server->getErrorNum(null)), + 'type'=>'error')); + } + } +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/refresh.php b/lam/templates/3rdParty/pla/htdocs/refresh.php new file mode 100644 index 00000000..385c4c6e --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/refresh.php @@ -0,0 +1,46 @@ +getIndex(),'tree'); + del_cached_item($app['server']->getIndex(),'tree'); + + if ($tree) + $openDNs = $tree->listOpenItems(); + else + $openDNs = array(); + + $tree = Tree::getInstance($app['server']->getIndex()); + + foreach ($openDNs as $value) { + $entry = $tree->getEntry($value); + if (! $entry) { + $tree->addEntry($value); + $entry = $tree->getEntry($value); + } + + $tree->readChildren($value,true); + $entry->open(); + } + + set_cached_item($app['server']->getIndex(),'tree','null',$tree); +} + +if (get_request('meth','REQUEST') == 'ajax') + header(sprintf('Location: cmd.php?cmd=draw_tree_node&noheader=%s&server_id=%s&meth=ajax&frame=TREE',get_request('noheader','REQUEST',false,0),$app['server']->getIndex())); +else + header(sprintf('Location: cmd.php?server_id=%s',$app['server']->getIndex())); + +die(); +?> diff --git a/lam/templates/3rdParty/pla/htdocs/rename.php b/lam/templates/3rdParty/pla/htdocs/rename.php new file mode 100644 index 00000000..96c2e33d --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/rename.php @@ -0,0 +1,64 @@ +getContainer($request['dnSRC']); + +# Error checking +if (! $app['server']->isBranchRenameEnabled()) { + # We search all children, not only the visible children in the tree + $children = $app['server']->getContainerContents($request['dnSRC'],null,0,'(objectClass=*)',LDAP_DEREF_NEVER); + + if (count($children) > 0) + error(_('You cannot rename an entry which has children entries (eg, the rename operation is not allowed on non-leaf entries)'),'error','index.php'); +} + +$request['dnDST'] = sprintf('%s,%s',$request['rdnDST'],$request['container']); + +if ($request['dnDST'] == $request['dnSRC']) + error(_('You did not change the RDN'),'error','index.php'); + +$rdnattr = array(); +$rdnattr['SRC'] = explode('=',$request['dnSRC']); +$rdnattr['SRC'] = $rdnattr['SRC'][0]; + +$new_dn_value = explode('=',$request['rdnDST'],2); +$rdnattr['DST'] = $new_dn_value[0]; + +if (count($new_dn_value) != 2 || ! isset($new_dn_value[1])) + error(_('Invalid RDN value'),'error','index.php'); + +$deleteoldrdn = $rdnattr['SRC'] == $rdnattr['DST']; +$success = $app['server']->rename($request['dnSRC'],$request['rdnDST'],$request['container'],$deleteoldrdn); + +if ($success) { + $rename_message = sprintf('%s',_('Rename successful!')); + $redirect_url = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s&template=%s', + $app['server']->getIndex(),rawurlencode($request['dnDST']),get_request('template','REQUEST')); + + system_message(array( + 'title'=>_('Rename Entry'), + 'body'=>$rename_message, + 'type'=>'info'), + $redirect_url); + +} else { + system_message(array( + 'title'=>_('Could not rename the entry.'), + 'body'=>ldap_error_msg($app['server']->getErrorMessage(null),$app['server']->getErrorNum(null)), + 'type'=>'error')); +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/rename_form.php b/lam/templates/3rdParty/pla/htdocs/rename_form.php new file mode 100644 index 00000000..aebc5fb1 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/rename_form.php @@ -0,0 +1,40 @@ +getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setDN($request['dn']); +$request['page']->accept(); + +# Render the form +$request['page']->drawTitle(sprintf('%s %s',_('Rename'),get_rdn($request['dn']))); +$request['page']->drawSubTitle(); + +echo '
'; +printf('%s %s %s:

',_('Rename'),get_rdn($request['dn']),_('to a new object')); + +echo '
'; +printf('',$app['server']->getIndex()); +printf('',rawurlencode($request['dn'])); +printf('',$request['template']); +printf('',get_rdn($request['dn'])); +printf('',_('Rename')); +echo '
'; + +echo '
'; +echo "\n"; +?> diff --git a/lam/templates/3rdParty/pla/htdocs/template_engine.php b/lam/templates/3rdParty/pla/htdocs/template_engine.php new file mode 100644 index 00000000..1cfaba98 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/template_engine.php @@ -0,0 +1,57 @@ +getIndex(),get_request('template','REQUEST',false,null)); + +# If we have a DN, then this is to edit the entry. +if ($request['dn']) { + $app['server']->dnExists($request['dn']) + or error(sprintf('%s (%s)',_('No such entry'),pretty_print_dn($request['dn'])),'error','index.php'); + + $request['page']->setDN($request['dn']); + $request['page']->accept(); + +} else { + if ($app['server']->isReadOnly()) + error(_('You cannot perform updates while server is in read-only mode'),'error','index.php'); + + $request['page']->setContainer(get_request('container','REQUEST')); + $request['page']->accept(); +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/update.php b/lam/templates/3rdParty/pla/htdocs/update.php new file mode 100644 index 00000000..45762cfb --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/update.php @@ -0,0 +1,78 @@ +getIndex(),rawurlencode($request['dn']))); + + die(); +} + +if (! $request['dn'] || ! $app['server']->dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$request['page'] = new PageRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setDN($request['dn']); +$request['page']->accept(); +$request['template'] = $request['page']->getTemplate(); + +# Perform the modification +$result = $app['server']->modify($request['dn'],$request['template']->getLDAPmodify()); + +if ($result) { + # Fire the post modification event to the user's custom callback function. + $mustRelogin = false; + + foreach ($request['template']->getLDAPmodify() as $attr_name => $val) { + /* Was this a user's password modification who is currently + * logged in? If so, they need to logout and log back in + * with the new password. */ + if (($attr_name == 'userpassword') && + in_array($app['server']->getValue('login','auth_type'),array('cookie','session')) && + pla_compare_dns($app['server']->getLogin(),$request['dn']) === 0) + + $mustRelogin = true; + } + + # If the user password was changed, not tell the to relogin. + if ($mustRelogin) { + $app['server']->logout('user'); + unset($_SESSION['ACTIVITY'][$app['server']->getIndex()]); + + system_message(array( + 'title'=>_('Modification successful!'), + 'body'=>_('Since you changed your password, you must now login again with your new password.'), + 'type'=>'info'), + sprintf('cmd.php?cmd=login_form&server_id=%s',$app['server']->getIndex())); + + exit; + } + + $redirect_url = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s', + $app['server']->getIndex(),$request['template']->getDNEncode()); + + foreach ($request['template']->getLDAPmodify() as $attr => $junk) + $redirect_url .= sprintf('&modified_attrs[]=%s',$attr); + + if (get_request('meth','REQUEST') == 'ajax') + $redirect_url .= '&meth=ajax'; + + header("Location: $redirect_url"); + die(); +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/update_confirm.php b/lam/templates/3rdParty/pla/htdocs/update_confirm.php new file mode 100644 index 00000000..037b91d8 --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/update_confirm.php @@ -0,0 +1,253 @@ +dnExists($request['dn'])) + error(sprintf(_('The entry (%s) does not exist.'),$request['dn']),'error','index.php'); + +$request['page'] = new PageRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->setDN($request['dn']); +$request['page']->accept(); +$request['template'] = $request['page']->getTemplate(); + +$request['page']->drawTitle(get_rdn($request['template']->getDN())); +$request['page']->drawSubTitle(); + +# Confirm the updates +if (count($request['template']->getLDAPmodify(true))) { + echo '
'; + echo _('Do you want to make these changes?'); + echo '

'; + echo '
'; + + echo "\n\n"; + echo '
'; + echo '
'; + echo ''; + printf('',$app['server']->getIndex()); + printf('',$request['template']->getDNEncode(false)); + echo "\n"; + + $request['page']->drawHiddenAttributes(); + echo '
'; + + echo ''; + echo "\n"; + + printf('', + _('Attribute'),_('Old Value'),_('New Value'),_('Skip')); + echo "\n\n"; + + # If we skip objectclass changes, but there are new must/may attrs provided by the new objectclass, they need to be skip. + $mustattrs = getMustAttrs($request['template']->getAttribute('objectclass')->getValues()); + + $counter = 0; + foreach ($request['template']->getLDAPmodify(true) as $attribute) { + $counter++; + + printf('',$counter%2 ? 'even' : 'odd'); + printf('',$attribute->getFriendlyName()); + + # Show OLD Values + echo ''; + + # Show NEW Values + echo ''; + + # Show SKIP Option + $input_disabled = ''; + $input_onclick = ''; + + if ($attribute->isForceDelete() || (in_array($attribute->getName(),$mustattrs)) && $request['template']->getAttribute('objectclass')->justModified()) + $input_disabled = 'disabled="disabled"'; + + if ($attribute->getName() == 'objectclass') { + $input_onclick = ''; + + # If there are attributes being force deleted... + if (count($request['template']->getForceDeleteAttrs()) > 0) { + $input_onclick = 'onclick="if (this.checked) {'; + + # And this OC is being skipped, then these attributes can be optionally deleted. + foreach ($request['template']->getForceDeleteAttrs() as $ad_name) { + # Only if it is not a must attr by this objectclass now staying + if (! in_array($ad_name->getName(),getMustAttrs($attribute->getOldValues()))) + $input_onclick .= sprintf("document.getElementById('skip_array_%s').disabled = false;",$ad_name->getName()); + + $input_onclick .= sprintf("document.getElementById('skip_array_%s').checked = true;",$ad_name->getName()); + $input_onclick .= "\n"; + } + + $input_onclick .= '} else {'; + + # Otherwise the attributes must be deleted. + foreach ($request['template']->getForceDeleteAttrs() as $ad_name) { + $input_onclick .= sprintf("document.getElementById('skip_array_%s').checked = false;",$ad_name->getName()); + $input_onclick .= sprintf("document.getElementById('skip_array_%s').disabled = true;",$ad_name->getName()); + $input_onclick .= "\n"; + } + + $input_onclick .= '};'; + } + + # If the attributes arent force deleted... + if ($input_onclick) + $input_onclick .= 'if (this.checked) {'; + else + $input_onclick = 'onclick="if (this.checked) {'; + + # IE: There are new objectclasses that result in new values. + foreach ($request['template']->getLDAPmodify(true) as $skipattr) { + if (! $skipattr->getOldValues()) { + if (! in_array($skipattr->getName(),$mustattrs)) + $input_onclick .= sprintf("document.getElementById('skip_array_%s').disabled = true;",$skipattr->getName()); + + $input_onclick .= sprintf("document.getElementById('skip_array_%s').checked = true;",$skipattr->getName()); + $input_onclick .= "\n"; + } + } + + $input_onclick .= '} else {'; + + foreach ($request['template']->getLDAPmodify(true) as $skipattr) { + if (! $skipattr->getOldValues()) { + if (! in_array($skipattr->getName(),$mustattrs)) + $input_onclick .= sprintf("document.getElementById('skip_array_%s').disabled = false;",$skipattr->getName()); + + $input_onclick .= sprintf("document.getElementById('skip_array_%s').checked = false;",$skipattr->getName()); + $input_onclick .= "\n"; + } + } + + $input_onclick .= '};"'; + } + + printf('', + htmlspecialchars($attribute->getName()),htmlspecialchars($attribute->getName()),$input_disabled,$input_onclick); + echo ''; + echo "\n\n"; + } + + echo '
%s%s%s%s
%s'; + + if (! $attribute->getOldValues()) + printf('[%s]',_('attribute doesnt exist')); + + $dv = $attribute->getRemovedValues(); + foreach ($attribute->getOldValues() as $key => $value) { + # For multiple values, we'll highlight the changed ones + if ($x = ((count($attribute->getOldValues()) > 5) && count($attribute->getValues()) && in_array($value,$dv))) + echo ''; + + $request['page']->draw('OldValue',$attribute,$key); + + # For multiple values, close the highlighting + if ($x) + echo ''; + + echo '
'; + } + + echo '
'; + + if (! $attribute->getValueCount() || $attribute->isForceDelete()) + printf('[%s]',_('attribute deleted')); + + $dv = $attribute->getAddedValues(); + foreach ($attribute->getValues() as $key => $value) { + # For multiple values, we'll highlight the changed ones + if ($x = ((count($attribute->getValues()) > 5) && count($attribute->getOldValues()) && in_array($value,$dv))) + echo ''; + + $request['page']->draw('CurrentValue',$attribute,$key); + + # For multiple values, close the highlighting + if ($x) + echo ''; + + echo '
'; + } + + echo '
'; + + echo '
'; + echo '
'; + // @todo cant use AJAX here, it affects file uploads. + printf('', + _('Update Object')); + + printf('', + _('Cancel'), + (isAjaxEnabled() ? sprintf('onclick="return ajDISPLAY(\'BODY\',\'cmd=template_engine&dn=%s\',\'%s\');"',htmlspecialchars($request['dn']),_('Retrieving DN')) : '')); + + echo '
'; + echo '
'; + echo '
'; + + if (count($request['template']->getForceDeleteAttrs()) > 0) { + echo ''; + printf('',_('The deletion of objectClass(es)')); + printf('',implode(', ',$request['template']->getAttribute('objectclass')->getRemovedValues())); + echo ''; + printf('',_('will delete the attribute(s)')); + echo '
%s:%s
%s:'; + + $i = 0; + foreach ($request['template']->getForceDeleteAttrs() as $attribute) { + if ($i++ != 0) + echo ', '; + + echo $_SESSION[APPCONFIG]->getFriendlyHTML($attribute); + } + echo '
'; + } + +} else { + $href = sprintf('cmd=template_engine&server_id=%s&dn=%s', + $app['server']->getIndex(),$request['template']->getDNEncode()); + + echo '
'; + echo _('You made no changes'); + + if (isAjaxEnabled()) + printf(' %s.', + htmlspecialchars($href),htmlspecialchars($href),_('Retrieving DN'),_('Go back')); + else + printf(' %s.',htmlspecialchars($href),_('Go back')); + + echo '
'; +} + +function getMustAttrs($oclasses) { + global $app; + + $mustattrs = array(); + + foreach ($oclasses as $value) { + $soc = $app['server']->getSchemaObjectClass($value); + + if ($soc) + foreach ($soc->getMustAttrs() as $sma) + array_push($mustattrs,$sma->getName()); + } + + return $mustattrs; +} +?> diff --git a/lam/templates/3rdParty/pla/htdocs/view_jpeg_photo.php b/lam/templates/3rdParty/pla/htdocs/view_jpeg_photo.php new file mode 100644 index 00000000..d52501cc --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/view_jpeg_photo.php @@ -0,0 +1,77 @@ +getDNAttrValues($request['dn'],null,LDAP_DEREF_NEVER,array($request['attr'])); + + break; +} + +if (! isset($jpeg_data[$request['attr']][$request['index']])) { + if (function_exists('imagecreate')) { + $im = imagecreate(160,30); + if (is_resource($im)) { + header('Content-type: image/png'); + + # Set the background + imagecolorallocatealpha($im,0xFC,0xFC,0xFE,127); + $text_color = imagecolorallocate($im,0,0,0); + imagestring($im,4,3,5,_('Image not available'),$text_color); + imagepng($im); + imagedestroy($im); + + die(); + } + } + + # We cant display an error, but we can set a system message, which will be display on the next page render. + system_message(array( + 'title'=>_('No image available'), + 'body'=>sprintf(_('Could not fetch jpeg data from LDAP server for attribute [%s].'),$request['attr']), + 'type'=>'warn')); + + die(); +} + +if (! is_array($jpeg_data[$request['attr']])) + $jpeg_data[$request['attr']] = array($jpeg_data[$request['attr']]); + +$obStatus = ob_get_status(); +if (isset($obStatus['type']) && $obStatus['type'] && $obStatus['status']) + ob_end_clean(); + +header(sprintf('Content-type: %s',$request['type'])); +header(sprintf('Content-disposition: inline; filename="%s"',$request['filename'])); +echo $jpeg_data[$request['attr']][$request['index']]; +die(); +?> diff --git a/lam/templates/3rdParty/pla/index.php b/lam/templates/3rdParty/pla/index.php new file mode 100644 index 00000000..716e6072 --- /dev/null +++ b/lam/templates/3rdParty/pla/index.php @@ -0,0 +1,11 @@ + diff --git a/lam/templates/3rdParty/pla/lib/AJAXTree.php b/lam/templates/3rdParty/pla/lib/AJAXTree.php new file mode 100644 index 00000000..300a9764 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/AJAXTree.php @@ -0,0 +1,307 @@ +" + * @param boolean $first_child is the first child entry, which is normally the "Create New Entry" option + * @param boolean $last_child is the last child entry, which is normally the "Create New Entry" option + */ + protected function draw_item($item,$level,$first_child=true,$last_child=true) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + # Level pre-treatment + $code = ''; + if (is_string($level)) { + for ($i=0; $i 0) + $code = '0' * $level; + + $level = strlen($code); + + # Get entry to display as node + $entry = $this->getEntry($item); + + # If the entry doesnt exist, we'll add it. + if (! $entry) { + $this->addEntry($item); + $entry = $this->getEntry($item); + } + + # If the entry doesnt exist in the server, then return here with an empty string. + if (! $entry) + return ''; + + # Get our children. + $child_count = $this->readChildrenNumber($entry->getDN()); + + $nb = 0; + if ($first_child) + $nb += 1; + if ($last_child) + $nb += 2; + + $imgs['expand'] = array('tree_expand.png','tree_expand.png','tree_expand_corner.png', + ($level > 0) ? 'tree_expand_corner.png' : 'tree_expand_corner_first.png'); + + $imgs['collapse'] = array('tree_collapse.png','tree_collapse.png','tree_collapse_corner.png', + ($level > 0) ? 'tree_collapse_corner.png' : 'tree_collapse_corner_first.png'); + + $imgs['tree'] = array('tree_split.png','tree_split.png','tree_corner.png','tree_corner.png'); + + /** Information on array[$nb] + * nb == 1 => the node is the first child + * nb == 2 => the node is the last child + * nb == 3 => the node is the unique child + * nb == 0 => the node is a child */ + $new_code = array('1','1','0','0'); + + # Links + $parms['openclose'] = htmlspecialchars(sprintf('server_id=%s&dn=%s&code=%s%s',$this->getServerID(),$entry->getDNEncode(),$code,$new_code[$nb])); + $parms['edit'] = htmlspecialchars(sprintf('cmd=template_engine&server_id=%s&dn=%s',$this->getServerID(),$entry->getDNEncode())); + $href = sprintf('cmd.php?%s',$parms['edit']); + + # Each node has a unique id based on dn + $node_id = sprintf('node%s',base64_encode(sprintf('%s-%s',$server->getIndex(),$entry->getDN()))); + $node_id = str_replace('=','_',$node_id); + + if ($level == 0) + printf('',$this->getDepth()+3-1); + + printf(''; + + printf('
',$node_id,($entry->isOpened() ? 'block' : 'none')); + if ($entry->isOpened()) + $this->draw_children($entry,$code.$new_code[$nb]); + + echo '
'; + + if ($level == 0) + echo ''; + } + + /** + * Expand and draw a child entry, when it is clicked on. This is using AJAX just to render this section of the tree. + */ + public function draw_children($parent_entry,$code) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $children = array(); + + foreach ($parent_entry->getChildren() as $child) { + if (! $this->getEntry($child)) + $this->addEntry($child); + + array_push($children,$this->getEntry($child)); + } + + $first_child = $this->create_before_child($parent_entry,$code); + $last_child = $this->create_after_child($parent_entry,$code); + + # If compression is on, we need to compress this output - but only if called by draw_tree_node + if (function_exists('isCompress') && isCompress() && get_request('cmd','REQUEST') == 'draw_tree_node') + ob_start(); + + echo $first_child; + + for ($i=0; $idraw_item($children[$i]->getDN(),$code,$first,$last); + else + echo '
problem getting DN entry from ldap'; + + echo "\n"; + } + + echo $last_child; + + # If compression is on, we need to compress this output + if (function_exists('isCompress') && isCompress() && get_request('cmd','REQUEST') == 'draw_tree_node') { + $output = ob_get_clean(); + echo gzencode($output); + } + } + + /** + * Return the indentation before a node + * + * @param $code a string of 0 and 1 ; $code == "000101" will return " | |" + */ + protected function get_indentation($code) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $indent = ''; + + for ($i=0; $i',IMGDIR); + break; + + case '1': + $indent .= sprintf('| ',IMGDIR); + break; + } + } + + return $indent; + } + + /** + * Draw the javascript to support the tree. + */ + protected function draw_javascript() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + parent::draw_javascript(); + printf('',JSDIR); + printf('',JSDIR); + } + + /** + * Draw the "Create New Entry" item before the children. + */ + private function create_before_child($entry,$level) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (strlen($level) == 0) + return ''; + + $server = $this->getServer(); + $output = ''; + + if (! $server->isReadOnly() && ! $entry->isLeaf() && (count($entry->getChildren()) > 10) && $this->getServer()->isShowCreateEnabled() + && $_SESSION[APPCONFIG]->getValue('appearance','show_top_create')) + $output = $this->draw_create_new_entry($entry,$level,IMGDIR.'/tree_split.png'); + + return $output; + } + + /** + * Draw the "Create New Entry" item after the children. + */ + private function create_after_child($entry,$level) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (strlen($level) == 0) + return ''; + + $server = $this->getServer(); + $output = ''; + + if (! $server->isReadOnly() && ! $entry->isLeaf() && $this->getServer()->isShowCreateEnabled()) + $output = $this->draw_create_new_entry($entry,$level,IMGDIR.'/tree_corner.png'); + + return $output; + } + + /** + * Draw the "Create New Entry" item. + */ + private function draw_create_new_entry($entry,$level,$img) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $output = ''; + + $href = sprintf('cmd=template_engine&server_id=%s&container=%s',$this->getServerID(),$entry->getDNEncode()); + + $output .= $this->get_indentation($level); + $output .= sprintf('--',$img); + $output .= sprintf('',htmlspecialchars($href),$entry->getDN()); + $output .= sprintf('->',IMGDIR); + $output .= ''; + $output .= ' '; + + if (isAjaxEnabled()) + $output .= sprintf('', + htmlspecialchars($href),_('Create new entry here'), + htmlspecialchars($href),_('Loading')); + else + $output .= sprintf('',htmlspecialchars($href),_('Create new entry here')); + + $output .= _('Create new entry here'); + $output .= ''; + + return $output; + } + + /** + * List the items in the tree that are open + * + * @return array List of open nodes + */ + public function listOpenItems() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + foreach ($this->entries as $dn => $value) + if ($value->isOpened()) + array_push($result,$value->getDN()); + + return $result; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/Attribute.php b/lam/templates/3rdParty/pla/lib/Attribute.php new file mode 100644 index 00000000..af134dff --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/Attribute.php @@ -0,0 +1,917 @@ +getServer($server_id); + + $sattr = $server->getSchemaAttribute($name); + if ($sattr) { + $this->name = $sattr->getName(false); + $this->setLDAPdetails($sattr); + + } else + $this->name = $name; + + $this->source = $source; + + # XML attributes are shown by default + switch ($source) { + case 'XML': $this->show(); + $this->setXML($values); + + break; + + default: + if (! isset($values['values'])) + debug_dump_backtrace('no index "values"',1); + + $this->initValue($values['values']); + } + + # Should this attribute be hidden + if ($server->isAttrHidden($this->name)) + $this->forcehide = true; + + # Should this attribute value be read only + if ($server->isAttrReadOnly($this->name)) + $this->readonly = true; + + # Should this attribute value be unique + if ($server->isAttrUnique($this->name)) + $this->unique = true; + } + + /** + * Return the name of the attribute. + * + * @param boolean $lower - Return the attribute in normal or lower case (default lower) + * @param boolean $real - Return the real attribute name (with ;binary, or just the name) + * @return string Attribute name + */ + public function getName($lower=true,$real=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->name); + + if ($real) + return $lower ? strtolower($this->name) : $this->name; + else + return $lower ? strtolower($this->real_attr_name()) : $this->real_attr_name(); + } + + public function getValues() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->values); + + return $this->values; + } + + public function getOldValues() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->oldvalues); + + return $this->oldvalues; + } + + public function getValueCount() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->values); + + return count($this->values); + } + + public function getSource() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->source); + + return $this->source; + } + + /** + * Autovalue is called after the attribute is initialised, and thus the values from the ldap server will be set. + */ + public function autoValue($new_val) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->values) + return; + + $this->values = $new_val; + } + + public function initValue($new_val) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->values || $this->oldvalues) { + debug_dump(array('new_val'=>$new_val,'this'=>$this)); + debug_dump_backtrace('new and/or old values are set',1); + } + + $this->values = $new_val; + } + + public function clearValue() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->values = array(); + } + + public function setOldValue($val) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->oldvalues = $val; + } + + public function setValue($new_val) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->values) { + if ($this->values == $new_val) + return; + + if ($this->oldvalues) { + debug_dump($this); + debug_dump_backtrace('old values are set',1); + } else + $this->oldvalues = $this->values; + } + + if ($new_val == $this->values) + return; + + $this->values = $new_val; + $this->justModified(); + } + + public function addValue($new_val,$i=-1) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($i < 0) + $i = $this->getValueCount(); + + $old_val = $this->getValue($i); + if (is_null($old_val) || ($old_val != $new_val)) + $this->justModified(); + + $this->values[$i] = $new_val; + } + + public function delValue($i=-1) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($i < 0) + $this->setValue(array()); + + if (! $this->hasBeenModified()) + $this->oldvalues = $this->values; + + if (isset($this->values[$i])) { + unset($this->values[$i]); + $this->values = array_values($this->values); + $this->justModified(); + } + } + + public function getValue($i) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($this->values[$i])) + return $this->values[$i]; + else + return null; + } + + public function getOldValue($i) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($this->oldvalues[$i])) + return $this->oldvalues[$i]; + else + return null; + } + + public function getMinValueCount() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->min_value_count); + + return $this->min_value_count; + } + + public function setMinValueCount($min) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->min_value_count = $min; + } + + public function getMaxValueCount() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->max_value_count); + + return $this->max_value_count; + } + + public function setMaxValueCount($max) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->max_value_count = $max; + } + + public function haveMoreValues() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->getMaxValueCount() < 0 || ($this->getValueCount() < $this->getMaxValueCount())) + return true; + else + return false; + } + + public function justModified() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->modified = true; + } + + public function hasBeenModified() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->modified); + + return $this->modified; + } + + public function isForceDelete() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->forcedelete); + + return $this->forcedelete; + } + + public function setForceDelete() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->forcedelete = true; + $this->oldvalues = $this->values; + $this->values = array(); + $this->justModified(); + } + + public function isInternal() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->internal); + + return $this->internal; + } + + public function setInternal() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->internal = true; + } + + public function isRequired() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->getMinValueCount() > 0) + return true; + elseif ($this->ldaptype == 'must') + return true; + elseif ($this->isRDN()) + return true; + else + return false; + } + + public function isMay() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (($this->ldaptype == 'may') && ! $this->isRequired()) + return true; + else + return false; + } + + public function setType($type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->type = strtolower($type); + } + + public function getType() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->type); + + return $this->type; + } + + public function setLDAPtype($type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->ldaptype = strtolower($type); + } + + public function getLDAPtype() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->ldaptype); + + return $this->ldaptype; + } + + public function setProperties($properties) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($properties as $index => $value) { + if ($index == 'maxvalnb') { + $this->setMaxValueCount($value); + continue; + + } elseif ($index == 'minvalnb') { + $this->setMinValueCount($value); + continue; + + } elseif ($index == 'maxlength') { + $this->setMinValueCount($value); + continue; + + } elseif ($index == 'hidden') { + $this->visible = $value; + continue; + + } elseif (in_array($index,array('cols','rows'))) { + # @todo To be implemented + continue; + } + + if (isset($this->$index)) + $this->$index = $value; + else { + debug_dump($this); + debug_dump_backtrace(sprintf('Unknown property (%s) with value (%s) for (%s)',$index,$value,$this->getName()),1); + } + } + } + + public function setRequired() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->getMinValueCount() <= 0) + $this->setMinValueCount(1); + } + + public function setOptional() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->setMinValueCount(0); + } + + public function isReadOnly() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->readonly); + + return $this->readonly; + } + + public function setReadOnly() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->readonly = true; + } + + public function isMultiple() { + return false; + } + + public function isVisible() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->visible && (! $this->forcehide); + } + + public function hide() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->visible = false; + } + + public function show() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->visible = true; + } + + public function haveFriendlyName() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $_SESSION[APPCONFIG]->haveFriendlyName($this); + } + + public function getFriendlyName() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->display); + + if ($this->display) + return $this->display; + else + return $_SESSION[APPCONFIG]->getFriendlyName($this); + } + + public function setDescription($description) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->description = $description; + } + + public function getDescription() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->description); + + return $this->description; + } + + public function setIcon($icon) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->icon = $icon; + } + + public function getIcon() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->icon); + + return $this->icon ? sprintf('%s/%s',IMGDIR,$this->icon) : ''; + } + + public function getHint() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->hint); + + return $this->hint; + } + + public function setHint($hint) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->hint = $hint; + } + + public function getMaxLength() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->maxlength); + + return $this->maxlength; + } + + public function setMaxLength($maxlength) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->maxlength = $maxlength; + } + + public function getSize() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->size); + + return $this->size; + } + + public function setSize($size) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->size = $size; + } + + public function getSpacer() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->spacer); + + return $this->spacer; + } + + public function getPage() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->page); + + return $this->page; + } + public function setPage($page) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->page = $page; + } + + public function getOnChange() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->onchange); + + return $this->onchange; + } + + public function getHelper() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->helper); + + return $this->helper; + } + + public function getHelperValue() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->helpervalue); + + return $this->helpervalue; + } + + public function getVerify() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->verify); + + return $this->verify; + } + + public function setRDN($rdn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->rdn = $rdn; + } + + /** + * Return if this attribute is an RDN attribute + * + * @return boolean + */ + public function isRDN() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->rdn); + + return $this->rdn; + } + + /** + * Capture all the LDAP details we are interested in + * + * @param sattr Schema Attribute + */ + private function setLDAPdetails($sattr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # By default, set this as a MAY attribute, later processing should make it a MUST attribute if it is. + if (! $this->ldaptype) + $this->ldaptype = 'may'; + + # Store our Aliases + foreach ($sattr->getAliases() as $alias) + array_push($this->aliases,strtolower($alias)); + + if ($sattr->getIsSingleValue()) + $this->setMaxValueCount(1); + } + + /** + * Return a list of aliases for this Attribute (as defined by the schema) + * This list will be lowercase. + */ + public function getAliases() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->aliases); + + return $this->aliases; + } + + public function getAutoValue() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->autovalue); + + return $this->autovalue; + } + + public function getPostValue() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->postvalue); + + return $this->postvalue; + } + + public function setPostValue($postvalue) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->postvalue = $postvalue; + } + + public function setXML($values) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Mostly all the time, this should be an array + if (is_array($values)) + foreach ($values as $index => $value) + switch ($index) { + # Helpers should be accompanied with a attribute. + case 'helper': + if (! isset($values['post']) && ! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Missing [post] setting in XML file'),$index), + 'body'=>_('[helper] needs an accompanying [post] action.'), + 'type'=>'warn')); + + if (isset($value['value']) && ! is_array($value['value']) && preg_match('/^=php\.(\w+)\((.*)\)$/',$value['value'],$matches)) { + $this->helpervalue['function'] = $matches[1]; + $this->helpervalue['args'] = $matches[2]; + + unset ($value['value']); + } + + foreach ($value as $i => $detail) { + if (! in_array($i,array('default','display','id','value'))) { + if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Unknown XML setting'),$i), + 'body'=>sprintf('%s [%s]',_('Unknown XML type setting for helper will be ignored.'),$detail), + 'type'=>'warn')); + + unset($value[$i]); + } + } + + $this->$index = $value; + + break; + + case 'hidden': $value ? $this->visible = false : $this->visible = true; + break; + + case 'spacer': $value ? $this->$index = true : $this->$index = false; + break; + + # Essentially, we ignore type, it is used to select an Attribute type in the Factory. But we'll generated a warning if there is an unknown type. + case 'type': + if (! in_array($value,array('password','multiselect','select','textarea')) && ! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Unknown XML setting'),$index), + 'body'=>sprintf('%s [%s]',_('Unknown XML type setting will be ignored.'),$value), + 'type'=>'warn')); + + break; + + case 'post': + if (preg_match('/^=php\.(\w+)\((.*)\)$/',$value,$matches)) { + $this->postvalue['function'] = $matches[1]; + $this->postvalue['args'] = $matches[2]; + + } else + if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Unknown XML setting'),$index), + 'body'=>sprintf('%s [%s]',_('Unknown XML type setting will be ignored.'),$value), + 'type'=>'warn')); + + case 'value': + if (is_array($value)) + foreach ($value as $y) { + if (! $this->haveMoreValues()) { + system_message(array( + 'title'=>_('Automatically removed attribute values from template'), + 'body'=>sprintf('%s [%s]',_('Template defines more values than can be accepted by attribute.'),$this->getName(true)), + 'type'=>'warn')); + + $this->clearValue(); + + break; + + } else + $this->addValue($y); + } + + else + # Check to see if the value is auto generated. + if (preg_match('/^=php\.(\w+)\((.*)\)$/',$value,$matches)) { + $this->autovalue['function'] = $matches[1]; + $this->autovalue['args'] = $matches[2]; + + # We'll add a hint too + if (! $this->hint) + $this->hint = _('Automatically determined'); + + } else + $this->addValue($value); + + break; + + # Queries + case 'ordersort': + + # Creation/Editing Templates + case 'cols': + case 'default': + case 'display': + case 'hint': + case 'icon': + case 'maxlength': + case 'onchange': + case 'order': + case 'page': + case 'readonly': + case 'rows': + case 'size': + case 'values': + case 'verify': $this->$index = $value; + break; + + case 'max': + if ($this->getMaxValueCount() == -1) + $this->setMaxValueCount($value); + + default: + if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Unknown XML setting'),$index), + 'body'=>sprintf('%s [%s]',_('Unknown attribute setting will be ignored.'),serialize($value)), + 'type'=>'warn')); + } + + elseif (is_string($values) && (strlen($values) > 0)) + $this->values = array($values); + } + + /** + * Display the values removed in an attribute. + */ + public function getRemovedValues() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return array_diff($this->getOldValues(),$this->getValues()); + } + + /** + * Display the values removed in an attribute. + */ + public function getAddedValues() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return array_diff($this->getValues(),$this->getOldValues()); + } + + /** + * Prunes off anything after the ";" in an attr name. This is useful for + * attributes that may have ";binary" appended to their names. With + * real_attr_name(), you can more easily fetch these attributes' schema + * with their "real" attribute name. + * + * @param string $attr_name The name of the attribute to examine. + * @return string + */ + private function real_attr_name() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name); + + return preg_replace('/;.*$/U','',$this->name); + } + + /** + * Does this attribute need supporting JS + */ + public function needJS($type=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (is_null($type)) { + foreach (array('focus','blur','validate') as $type) + if ($this->needJS($type)) + return true; + + return false; + + } elseif ($type == 'focus') { + # We dont have any focus javascript routines. + return false; + + } elseif ($type == 'blur') { + if ($this->onchange || $this->isRequired()) + return true; + else + return false; + + } elseif ($type == 'validate') { + if ($this->isRequired()) + return true; + else + return false; + + } else + debug_dump_backtrace(sprintf('Unknown JS request %s',$type),1); + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/AttributeFactory.php b/lam/templates/3rdParty/pla/lib/AttributeFactory.php new file mode 100644 index 00000000..82cf5627 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/AttributeFactory.php @@ -0,0 +1,188 @@ +getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Unknown template [post] function'),$matches[1]), + 'body'=>sprintf('%s [%s]',_('The template function is not known and will be ignored.'),$values['post']), + 'type'=>'warn')); + + unset($values['post']); + } + } + } + + # Check our helper functions exists + if (isset($values['helper']['value']) && ! is_array($values['helper']['value'])) + if (preg_match('/^=php\.(\w+)\((.*)\)$/',$values['helper']['value'],$matches)) + if (! in_array($matches[1],array('GetNextNumber','PasswordEncryptionTypes'))) { + if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Unknown template helper function'),$matches[1]), + 'body'=>sprintf('%s [%s]',_('The template helper function is not known and will be ignored.'),$values['helper']['value']), + 'type'=>'warn')); + + unset($values['helper']['value']); + } + + # Check to see if the value is auto generated, our attribute type is dependant on the function called. + if (isset($values['value']) && ! is_array($values['value'])) { + if (preg_match('/^=php\.(\w+)\((.*)\)$/',$values['value'],$matches)) { + switch ($matches[1]) { + case 'MultiList': + if (! isset($values['type'])) + $values['type'] = 'multiselect'; + + case 'PickList': + return $this->newSelectionAttribute($name,$values,$server_id,$source); + + case 'RandomPassword': + return $this->newRandomPasswordAttribute($name,$values,$server_id,$source); + + # Fall through and determine the attribute using other methods. + case 'GetNextNumber': + case 'Function' : + break; + + default: + if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>sprintf('%s [%s]',_('Unknown template function'),$matches[1]), + 'body'=>sprintf('%s [%s]',_('The template function is not known and will be ignored.'),$values['value']), + 'type'=>'warn')); + + unset($values['value']); + } + } + } + + if (isset($values['type'])) + switch ($values['type']) { + case 'password': + if (! strcasecmp($name,'sambaLMPassword') || ! strcasecmp($name,'sambaNTPassword')) + return $this->newSambaPasswordAttribute($name,$values,$server_id,$source); + else + return $this->newPasswordAttribute($name,$values,$server_id,$source); + + case 'multiselect': + case 'select': + return $this->newSelectionAttribute($name,$values,$server_id,$source); + + case 'textarea': + return $this->newMultiLineAttribute($name,$values,$server_id,$source); + } + + if (! strcasecmp($name,'objectClass')) { + return $this->newObjectClassAttribute($name,$values,$server_id,$source); + + } elseif ($app['server']->isJpegPhoto($name) || in_array($name,$app['server']->getValue('server','jpeg_attributes'))) { + return $this->newJpegAttribute($name,$values,$server_id,$source); + + } elseif ($app['server']->isAttrBinary($name)) { + return $this->newBinaryAttribute($name,$values,$server_id,$source); + + } elseif (! strcasecmp($name,'userPassword')) { + return $this->newPasswordAttribute($name,$values,$server_id,$source); + + } elseif (! strcasecmp($name,'sambaLMPassword') || ! strcasecmp($name,'sambaNTPassword')) { + return $this->newSambaPasswordAttribute($name,$values,$server_id,$source); + + } elseif (in_array(strtolower($name),array_keys(array_change_key_case($_SESSION[APPCONFIG]->getValue('appearance','date_attrs'))))) { + return $this->newDateAttribute($name,$values,$server_id,$source); + + } elseif (in_array(strtolower($name),array('shadowlastchange','shadowmin','shadowmax','shadowexpire','shadowwarning','shadowinactive'))) { + return $this->newShadowAttribute($name,$values,$server_id,$source); + + } elseif ($app['server']->isAttrBoolean($name)) { + $attribute = $this->newSelectionAttribute($name,$values,$server_id,$source); + $attribute->addOption('TRUE',_('true')); + $attribute->addOption('FALSE',_('false')); + return $attribute; + + } elseif ($app['server']->isDNAttr($name)) { + return $this->newDnAttribute($name,$values,$server_id,$source); + + } elseif ($app['server']->isMultiLineAttr($name)) { + return $this->newMultiLineAttribute($name,$values,$server_id,$source); + + } elseif (! strcasecmp($name,'gidNumber')) { + return $this->newGidAttribute($name,$values,$server_id,$source); + + } else { + return new Attribute($name,$values,$server_id,$source); + } + } + + private function newJpegAttribute($name,$values,$server_id,$source) { + return new JpegAttribute($name,$values,$server_id,$source); + } + + private function newBinaryAttribute($name,$values,$server_id,$source) { + return new BinaryAttribute($name,$values,$server_id,$source); + } + + private function newPasswordAttribute($name,$values,$server_id,$source) { + return new PasswordAttribute($name,$values,$server_id,$source); + } + + private function newSambaPasswordAttribute($name,$values,$server_id,$source) { + return new SambaPasswordAttribute($name,$values,$server_id,$source); + } + + private function newRandomPasswordAttribute($name,$values,$server_id,$source) { + return new RandomPasswordAttribute($name,$values,$server_id,$source); + } + + private function newShadowAttribute($name,$values,$server_id,$source) { + return new ShadowAttribute($name,$values,$server_id,$source); + } + + private function newSelectionAttribute($name,$values,$server_id,$source) { + return new SelectionAttribute($name,$values,$server_id,$source); + } + + private function newMultiLineAttribute($name,$values,$server_id,$source) { + return new MultiLineAttribute($name,$values,$server_id,$source); + } + + private function newDateAttribute($name,$values,$server_id,$source) { + return new DateAttribute($name,$values,$server_id,$source); + } + + private function newObjectClassAttribute($name,$values,$server_id,$source) { + return new ObjectClassAttribute($name,$values,$server_id,$source); + } + + private function newDnAttribute($name,$values,$server_id,$source) { + return new DnAttribute($name,$values,$server_id,$source); + } + + private function newGidAttribute($name,$values,$server_id,$source) { + return new GidAttribute($name,$values,$server_id,$source); + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/BinaryAttribute.php b/lam/templates/3rdParty/pla/lib/BinaryAttribute.php new file mode 100644 index 00000000..6b5053be --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/BinaryAttribute.php @@ -0,0 +1,60 @@ +filepaths = array(); + $this->filenames = array(); + } + + public function getFileNames() { + return $this->filenames; + } + + public function getFileName($i) { + if (isset($this->filenames[$i])) return $this->filenames[$i]; + else return null; + } + + public function addFileName($name, $i = -1) { + if ($i < 0) { + $this->filenames[] = $name; + } else { + $this->filenames[$i] = $name; + } + } + + public function getFilePaths() { + return $this->filepaths; + } + + public function getFilePath($i) { + if (isset($this->filepaths[$i])) return $this->filepaths[$i]; + else return null; + } + + public function addFilePath($path, $i = -1) { + if ($i < 0) { + $this->filepaths[] = $path; + } else { + $this->filepaths[$i] = $path; + } + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/DateAttribute.php b/lam/templates/3rdParty/pla/lib/DateAttribute.php new file mode 100644 index 00000000..e1a687f2 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/DateAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/DnAttribute.php b/lam/templates/3rdParty/pla/lib/DnAttribute.php new file mode 100644 index 00000000..1f5c02f4 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/DnAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/GidAttribute.php b/lam/templates/3rdParty/pla/lib/GidAttribute.php new file mode 100644 index 00000000..7442a079 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/GidAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/HTMLTree.php b/lam/templates/3rdParty/pla/lib/HTMLTree.php new file mode 100644 index 00000000..dc870632 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/HTMLTree.php @@ -0,0 +1,588 @@ +getServer(); + + echo ''; + if (! $onlytree) + $this->draw_server_name(); + + $this->javascript = ''; + $javascript_id = 0; + + /* Do we have what it takes to authenticate here, or do we need to + * present the user with a login link (for 'cookie' and 'session' auth_types)? + */ + if ($server->isLoggedIn(null)) { + if (! $onlytree) { + $this->draw_menu(); + + if ($server->getAuthType() != 'config') + $this->draw_logged_in_user(); + else + printf('',$this->getDepth()+3); + + if ($server->isReadOnly()) + printf('',$this->getDepth()+3-1,_('read only')); + else + printf('',$this->getDepth()+3); + + printf(''; + + echo '
 
(%s)
 
 
', + $_SESSION[APPCONFIG]->getValue('appearance','tree_width') ? sprintf('width: %spx; ',$_SESSION[APPCONFIG]->getValue('appearance','tree_width')) : '', + $_SESSION[APPCONFIG]->getValue('appearance','tree_height') ? sprintf('height: %spx; ',$_SESSION[APPCONFIG]->getValue('appearance','tree_height')) : '', + $server->getIndex()); + } + + echo ''; + + if (! count($this->getBaseEntries())) { + # We didnt get any baseDN entries in our tree? + printf('', + $this->getDepth()+3-2, + _('Could not determine the root of your LDAP tree.'), + _('It appears that the LDAP server has been configured to not reveal its root.'), + _('Please specify it in config.php')); + + echo '
%s
%s
%s
'; + + if (! $onlytree) + echo '
'; + return; + } + + /** + * Check if the LDAP server is not yet initialized + * (ie, the base DN configured in config.php does not exist) + */ + foreach ($this->getBaseEntries() as $base) { + if (! $base->isInLDAP()) { + $js_drawn = false; + $javascript_id++; + + $rdn = explode('=',get_rdn($base->getDN())); + printf('%s', + IMGDIR,$this->getDepth()+3-3,pretty_print_dn($base->getDN())); + + $this->javascript .= sprintf('
',$server->getIndex(),$javascript_id); + $this->javascript .= '
'; + $this->javascript .= ''; + $this->javascript .= sprintf('',$server->getIndex()); + $this->javascript .= sprintf('',htmlspecialchars($server->getContainer($base->getDN()))); + $this->javascript .= sprintf('',get_rdn($base->getDN())); + $this->javascript .= sprintf('',$rdn[0]); + $this->javascript .= sprintf('',$rdn[0],$rdn[1]); + $this->javascript .= ''; + $this->javascript .= ''; + $this->javascript .= '
'; + $this->javascript .= sprintf('
'); + + if (preg_match('/,/',$base->getDN())) + printf('%s', + $this->getDepth()+3-3,_('This base cannot be created with PLA.')); + else + printf('%s %s', + $this->getDepth()+3-3,_('This base entry does not exist.'),$server->getIndex(),$javascript_id,_('Create it?')); + + } else { + $this->draw_item($base->getDN(),-1); + } + } + + echo ''; + + if (! $onlytree) + echo ''; + + # We are not logged in, draw a login... link. + } else { + switch ($server->getAuthType()) { + case 'cookie': + case 'http': + case 'session': + $this->draw_login_link(); + break; + + case 'config': + case 'proxy': + case 'sasl': + break; + + default: + die(sprintf('Error: %s hasnt been configured for auth_type %s',__METHOD__,$server->getAuthType())); + } + } + + # Tree Footer. + echo ''; + echo "\n\n"; + + if (! $js_drawn) { + $this->draw_javascript(); + $js_drawn = true; + } + } + + /** + * Draw the server name + */ + protected function draw_server_name() { + return; + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + echo ''; + printf('%s',IMGDIR,_('Server')); + printf('',$this->getDepth()+3-1); + printf('%s',$server->getName()); + + if (! is_null($server->inactivityTime())) { + $m = sprintf(_('Inactivity will log you off at %s'), + strftime('%H:%M',$server->inactivityTime())); + printf(' %s',IMGDIR,$m,'Timeout'); + } + echo ''; + } + + /** + * Draw the tree menu options + */ + protected function draw_menu() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $links = ''; + + if (is_array($_SESSION[APPCONFIG]->getValue('menu','session'))) + foreach ($_SESSION[APPCONFIG]->getValue('menu','session') as $link => $title) { + if ($this->get_menu_item($link)) + $links .= sprintf('%s',$this->get_menu_item($link)); + } + + # Finally add our logout link. + $links .= sprintf('%s',$this->get_logout_menu_item()); + + # Draw the quick-links below the server name: + if ($links) { + printf('',$this->getDepth()+3-1); + printf('%s
',$links); + echo ''; + } + } + + /** + * Get the HTML for each tree menu option + */ + protected function get_menu_item($item) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $menu = array(); + + switch($item) { + case 'schema': + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','schema')) + return ''; + + $menu['cmd'] = 'schema'; + $menu['ajax'] = _('Loading Schema'); + $menu['div'] = 'BODY'; + $menu['title'] = _('View schema for'); + $menu['img'] = 'schema-big.png'; + $menu['name'] = _('schema'); + + break; + + case 'search': + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','query_engine')) + return ''; + + $menu['cmd'] = 'query_engine'; + $menu['ajax'] = _('Loading Search'); + $menu['div'] = 'BODY'; + $menu['title'] = _('Search'); + $menu['img'] = 'search-big.png'; + $menu['name'] = _('search'); + + break; + + case 'refresh': + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','refresh')) + return ''; + + $menu['cmd'] = 'refresh'; + $menu['href'] = '&noheader=1&purge=1'; + $menu['ajax'] = _('Refreshing Tree'); + $menu['div'] = sprintf('SID_%s_nodes',$server->getIndex()); + $menu['title'] = _('Refresh'); + $menu['img'] = 'refresh-big.png'; + $menu['name'] = _('refresh'); + + break; + + case 'server_info': + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','server_info')) + return ''; + + $menu['cmd'] = 'server_info'; + $menu['ajax'] = _('Loading Info'); + $menu['div'] = 'BODY'; + $menu['title'] = _('Info'); + $menu['img'] = 'info-big.png'; + $menu['name'] = _('info'); + + break; + + case 'monitor': + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','monitor')) + return ''; + + $attrs = $server->getRootDSE(); + if (! $attrs || ! isset($attrs['monitorcontext'])) + return ''; + + $menu['cmd'] = 'monitor'; + $menu['ajax'] = _('Loading Monitor Info'); + $menu['div'] = 'BODY'; + $menu['title'] = _('Monitor'); + $menu['img'] = 'monitorserver-big.png'; + $menu['name'] = _('monitor'); + + break; + + case 'import': + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','import_form') || ! $_SESSION[APPCONFIG]->isCommandAvailable('script','import') || $server->isReadOnly()) + return ''; + + $menu['cmd'] = 'import_form'; + $menu['ajax'] = _('Loading Import'); + $menu['div'] = 'BODY'; + $menu['title'] = _('Import'); + $menu['img'] = 'import-big.png'; + $menu['name'] = _('import'); + + break; + + case 'export': + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','export_form') || ! $_SESSION[APPCONFIG]->isCommandAvailable('script','export')) + return ''; + + $menu['cmd'] = 'export_form'; + $menu['ajax'] = _('Loading Export'); + $menu['div'] = 'BODY'; + $menu['title'] = _('Export'); + $menu['img'] = 'export-big.png'; + $menu['name'] = _('export'); + + break; + + default: + return false; + } + + $href_parms = htmlspecialchars(sprintf('cmd=%s&server_id=%s%s',$menu['cmd'],$server->getIndex(),isset($menu['href']) ? $menu['href'] : '')); + + if (isAjaxEnabled()) + return sprintf('%s
%s
', + $href_parms,$menu['div'],$href_parms,$menu['ajax'],$menu['title'],$server->getName(),IMGDIR,$menu['img'],$menu['name'],$menu['name']); + else + return sprintf('%s
%s
', + $href_parms,$menu['title'],$server->getName(),IMGDIR,$menu['img'],$menu['name'],$menu['name']); + } + + protected function get_logout_menu_item() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $href = sprintf('cmd.php?cmd=logout&server_id=%s',$server->getIndex()); + + if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','logout') || in_array($server->getAuthType(),array('config','http','proxy'))) + return ''; + else + return sprintf('%s
%s
', + htmlspecialchars($href),_('Logout of this server'),IMGDIR,'logout-big.png',_('logout'),_('logout')); + } + + /** + * Draw the Logged in User + */ + protected function draw_logged_in_user() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + $logged_in_dn = $server->getLogin(null); + echo ''; + echo ''; + printf('%s: ',$this->getDepth()+3-1,_('Logged in as')); + + if ($server->getContainerTop($logged_in_dn) == $logged_in_dn) { + $logged_in_branch = ''; + $logged_in_dn_array = array(); + + } else { + $logged_in_branch = preg_replace('/,'.$server->getContainerTop($logged_in_dn).'$/','',$logged_in_dn); + $logged_in_dn_array = pla_explode_dn($logged_in_branch); + } + + $bases = $server->getContainerTop($logged_in_dn); + if (is_array($bases) && count($bases)) + array_push($logged_in_dn_array,$bases); + + $rdn = $logged_in_dn; + + # Some sanity checking here, in case our DN doesnt look like a DN + if (! is_array($logged_in_dn_array)) + $logged_in_dn_array = array($logged_in_dn); + + if (trim($logged_in_dn)) { + if ($server->dnExists($logged_in_dn)) + foreach ($logged_in_dn_array as $rdn_piece) { + $href = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s',$server->getIndex(),rawurlencode($rdn)); + printf('%s',htmlspecialchars($href),pretty_print_dn($rdn_piece)); + + if ($rdn_piece != end($logged_in_dn_array)) + echo ','; + + $rdn = substr($rdn,(1 + strpos($rdn,','))); + } + + else + echo $logged_in_dn; + + } else { + echo 'Anonymous'; + } + + echo ''; + echo ''; + } + + /** + * Recursively descend on the given dn and draw the tree in html + * + * @param dn $dn Current dn. + * @param int $level Level to start drawing (start to -1) + */ + protected function draw_item($item,$level) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + # Get entry to display as node + $entry = $this->getEntry($item); + + # If the entry doesnt exist, we'll add it. + if (! $entry) { + $this->addEntry($item); + $entry = $this->getEntry($item); + } + + # If the entry doesnt exist in the server, then return here with an empty string. + if (! $entry) + return; + + # Get our children. + $child_count = $this->readChildrenNumber($item); + + $rdn = get_rdn($item); + $dnENCODE = rawurlencode($item); + $href['expand'] = htmlspecialchars(sprintf('cmd.php?cmd=expand&server_id=%s&dn=%s',$server->getIndex(),$dnENCODE)); + $href['collapse'] = htmlspecialchars(sprintf('cmd.php?cmd=collapse&server_id=%s&dn=%s',$server->getIndex(),$dnENCODE)); + $href['edit'] = htmlspecialchars(sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s',$server->getIndex(),$dnENCODE)); + + echo ''; + printf('',$level+2); + + # Is this node expanded? (deciding whether to draw "+" or "-") + if ($entry->isOpened()) + if (! $child_count && ! $this->getServer()->isShowCreateEnabled()) + printf('-',IMGDIR); + else + printf('-',$href['collapse'],IMGDIR); + else + if (($child_count !== false) && (! $child_count) && (! $this->getServer()->isShowCreateEnabled())) + printf('-',IMGDIR); + else + printf('+',$href['expand'],IMGDIR); + + printf('img', + $href['edit'],$server->getIndex(),preg_replace('/=/','_',base64_encode($item)),IMGDIR,$entry->getIcon()); + + printf('',$this->getDepth()+3-$level); + printf('%s',$href['edit'],$this->get_formatted_dn($entry,$level)); + + if ($child_count) + printf(' (%s)',$child_count); + + echo ''; + + if ($entry->isOpened()) { + /* Draw the "create new" link at the top of the tree list if there are more than 10 + * entries in the listing for this node. + */ + if (!$server->isReadOnly() && (count($entry->getChildren()) > 10) + && $this->getServer()->isShowCreateEnabled()) { + + $this->draw_create_link($rdn,$level,$dnENCODE); + } + + foreach ($entry->getChildren() as $dnChildEntry) + $this->draw_item($dnChildEntry,$level+1); + + # Always draw the "create new" link at the bottom of the listing + if (! $server->isReadOnly() && ! $entry->isLeaf() && $this->getServer()->isShowCreateEnabled()) { + $this->draw_create_link($rdn,$level,$dnENCODE); + } + } + + if (DEBUG_ENABLED) + debug_log('Leaving (%s,%s)',33,0,__FILE__,__LINE__,__METHOD__,$item,$level); + } + + protected function get_formatted_dn($entry,$level) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($level < 0) + return pretty_print_dn($entry->getDN()); + else + return draw_formatted_dn($this->getServer(),$entry); + } + + /** + * Print the HTML to show the "create new entry here". + * + * @param dn $rdn + * @param int $level + * @param dn $encoded_dn + */ + protected function draw_create_link($rdn,$level,$encoded_dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # print the "Create New object" link. + $href = htmlspecialchars(sprintf('cmd.php?cmd=template_engine&server_id=%s&container=%s',$this->getServerID(),$encoded_dn)); + + echo ''; + printf('',$level+3); + printf('%s',$href,IMGDIR,_('new')); + printf('%s', + $this->getDepth()+3-$level,$href,_('Create a new entry in'),$rdn,_('Create new entry here')); + echo ''; + } + + /** + * Draw login link + */ + protected function draw_login_link() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $href_parm = htmlspecialchars(sprintf('cmd=%s&server_id=%s',get_custom_file($server->getIndex(),'login_form',''),$server->getIndex())); + + echo ''; + + if (isAjaxEnabled()) { + printf('%s', + $href_parm,$href_parm,_('Loading Login'),_('Login to'),$server->getName(),IMGDIR,'login.png',_('login')); + printf('%s', + $this->getDepth()+3-2,$href_parm,$href_parm,_('Loading Login'),_('Login to'),$server->getName(),_('login')); + + } else { + printf('%s',$href_parm,IMGDIR,'login.png',_('login')); + printf('%s...',$this->getDepth()+3-2,$href_parm,_('Login')); + } + + echo ''; + + printf(' ',$this->getDepth()+3); + printf(' ',$this->getDepth()+3); + } + + /** + * If there is javascript, draw it + */ + protected function draw_javascript() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->javascript) { + echo "\n"; + echo $this->javascript; + echo "\n"; + $this->javascript = ''; + } + } + + /** + * Work out how deep the "opened" tree is. + */ + public function getDepth() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + # If we are not logged in + if (! $server->isLoggedIn(null)) + return 0; + + static $depths = array(); + + if (! isset($depths[$server->getIndex()])) { + $max = 0; # BaseDN are open, so we start at 1. + + foreach ($this->entries as $dn) { + $basedepth = count(pla_explode_dn($server->getContainerPath($dn->getDN(),'/'))); + $depth = 0; + + $depth = count(pla_explode_dn($dn->getDN()))+1-$basedepth; + + if ($depth > $max) + $max = $depth; + } + + $depths[$server->getIndex()] = $max; + } + + return $depths[$server->getIndex()]; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/JpegAttribute.php b/lam/templates/3rdParty/pla/lib/JpegAttribute.php new file mode 100644 index 00000000..d4400480 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/JpegAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/MassRender.php b/lam/templates/3rdParty/pla/lib/MassRender.php new file mode 100644 index 00000000..f0bf67e5 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/MassRender.php @@ -0,0 +1,48 @@ +%s
',__METHOD__); + + $val = $attribute->getValue($i); + + if ($attribute->getHelper()) + echo '
'; + + printf('', + $j,htmlspecialchars($attribute->getName()),$i, + $j,htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($val), + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + ($attribute->getSize() > 0) ? sprintf('size="%s"',$attribute->getSize()) : '', + ($attribute->getMaxLength() > 0) ? sprintf('maxlength="%s"',$attribute->getMaxLength()) : ''); + + if ($attribute->getHelper()) { + echo ''; + $this->draw('AttributeHelper',$attribute,$i); + echo '
'; + } + } + + protected function drawMassFormReadWriteValueBinaryAttribute($attribute,$i,$j) { + $this->drawFormReadWriteValueBinaryAttribute($attribute,$i); + } + + protected function drawMassFormReadWriteValueJpegAttribute($attribute,$i,$j) { + $this->drawFormReadOnlyValueJpegAttribute($attribute,$i); + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/MultiLineAttribute.php b/lam/templates/3rdParty/pla/lib/MultiLineAttribute.php new file mode 100644 index 00000000..b6d0eb52 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/MultiLineAttribute.php @@ -0,0 +1,35 @@ +rows; + } + + public function setRows($rows) { + $this->rows = $rows; + } + + public function getCols() { + return $this->cols; + } + + public function setCols($cols) { + $this->cols = $cols; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/ObjectClassAttribute.php b/lam/templates/3rdParty/pla/lib/ObjectClassAttribute.php new file mode 100644 index 00000000..939094dc --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/ObjectClassAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/PageRender.php b/lam/templates/3rdParty/pla/lib/PageRender.php new file mode 100644 index 00000000..cad0e49a --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/PageRender.php @@ -0,0 +1,1225 @@ +server_id = $server_id; + $this->template_id = $template_id; + } + + /** + * Dummy method... + */ + protected function visitAttribute() {} + + /** + * Get our templates applicable for this object + */ + protected function getTemplates() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return new Templates($this->server_id); + } + + public function getTemplate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->template; + } + + public function getTemplateID() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->template->getID(); + } + + /** + * Initialise the PageRender + */ + public function accept() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s:%s
',time(),__METHOD__); + + if ($this->template_id) { + $templates = $this->getTemplates(); + $this->template = $templates->getTemplate($this->template_id); + + if ($this->dn) + $this->template->setDN($this->dn); + elseif ($this->container) + $this->template->setContainer($this->container); + + $this->template->accept(); + + # Process our actions + if (get_request('post_value','REQUEST')) + foreach (get_request('post_value','REQUEST') as $attr => $values) { + $attribute = $this->template->getAttribute($attr); + + if (! $attribute) + debug_dump_backtrace(sprintf('There was a post_value for an attribute [%s], but it doesnt exist?',$attr),1); + + foreach ($values as $index) + if ($attribute->getPostValue()) + $this->get('Post',$attribute,$index); + else + $this->get('AutoPost',$attribute,$index); + } + + foreach ($this->template->getAttributes(true) as $attribute) { + if (DEBUGTMP||DEBUGTMPSUB) printf('* %s [Accept:%s]
',__METHOD__,get_class($attribute)); + + $this->visit('',$attribute); + } + } + } + + public function drawTitle($title='Title') { + printf('

%s

',$title); + } + + public function drawSubTitle($subtitle=null) { + if (is_null($subtitle)) + $subtitle = sprintf('%s: %s', + _('DN'),$this->dn); + + printf('

%s

',$subtitle); + } + + public function setDN($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->container) + system_message(array( + 'title'=>__METHOD__, + 'body'=>'CONTAINER set while setting DN', + 'type'=>'info')); + + $this->dn = $dn; + } + + public function setContainer($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->dn) + system_message(array( + 'title'=>__METHOD__, + 'body'=>'DN set while setting CONTAINER', + 'type'=>'info')); + + $this->container = $dn; + } + + /** + * May be overloaded in other classes + */ + protected function getMode() {} + protected function getModeContainer() {} + + /** + * Process our arguments from the templates + */ + protected function getPostAttribute($attribute,$i) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $autovalue = $attribute->getPostValue(); + $args = explode(';',$autovalue['args']); + $server = $this->getServer(); + $vals = $attribute->getValues(); + + switch ($autovalue['function']) { + /** + * Join will concatenate values with a string, similiar to explode() + * eg: =php.Join(-;%sambaSID%,%sidsuffix%) + * + * * arg 0 + * - character to use when joining the attributes + * + * * arg 1 + * - values to concatenate together. we'll explode %attr% values. + */ + case 'Join': + preg_match_all('/%(\w+)(\|.+)?(\/[lU])?%/U',$args[1],$matchall); + $matchattrs = $matchall[1]; + $char = $args[0]; + + $values = array(); + $blank = 0; + foreach ($matchattrs as $joinattr) { + $attribute2 = $this->template->getAttribute($joinattr); + + if (! $attribute2) { + if (($pv = get_request(strtolower($joinattr),'REQUEST')) && isset($pv[$attribute->getName()][$i])) { + array_push($values,$pv[$attribute->getName()][$i]); + + if (! $pv[$attribute->getName()][$i]) + $blank++; + + } else { + array_push($values,''); + $blank++; + } + + } elseif (count($attribute2->getValues()) == 0) { + return; + + } elseif (count($attribute2->getValues()) != 1) { + array_push($values,''); + $blank++; + + system_message(array( + 'title'=>_('Invalid value count for [post] processing'), + 'body'=>sprintf('%s (%s [%s])',_('Function() variable expansion can only handle 1 value'), + $attribute->getName(false),count($attribute->getValues())), + 'type'=>'warn')); + + } else + array_push($values,$attribute2->getValue(0)); + } + + # If all our value expansion results in blanks, we'll return no value + if (count($matchattrs) == $blank) + if (count($vals) > 1) + $vals[$i] = null; + else + $vals = null; + + else + $vals[$i] = implode($char,$values); + + break; + + /** + * PasswordEncrypt will encrypt a password + * eg: =php.PasswordEncrypt(%enc%;%userPassword%) + * + * This function will encrypt the users password "userPassword" using the "enc" method. + */ + case 'PasswordEncrypt': + if (count($args) != 2) { + system_message(array( + 'title'=>_('Invalid argument count for PasswordEncrypt'), + 'body'=>sprintf('%s (%s)',_('PasswordEncrypt() only accepts two arguments'),$autovalue['args']), + 'type'=>'warn')); + + return; + } + + if (! $attribute->hasBeenModified()) + return; + + # Get the attribute. + if (preg_match_all('/%(\w+)(\|.+)?(\/[lU])?%/U',strtolower($args[1]),$matchall)) { + if (count($matchall[1]) != 1) + system_message(array( + 'title'=>_('Invalid value count for PasswordEncrypt'), + 'body'=>sprintf('%s (%s)',_('Unable to get the attribute value for PasswordEncrypt()'),count($matchall[1])), + 'type'=>'warn')); + + $passwordattr = $matchall[1][0]; + $passwordvalue = $_REQUEST['new_values'][$passwordattr][$i]; + + } else + $passwordvalue = $args[1]; + + if (! trim($passwordvalue) || in_array($passwordvalue,$attribute->getOldValues())) + return; + + # Get the encoding + if ($passwordattr && preg_match_all('/%(\w+)(\|.+)?(\/[lU])?%/U',strtolower($args[0]),$matchall)) { + if (count($matchall[1]) != 1) + system_message(array( + 'title'=>_('Invalid value count for PasswordEncrypt'), + 'body'=>sprintf('%s (%s)',_('Unable to get the attribute value for PasswordEncrypt()'),count($matchall[1])), + 'type'=>'warn')); + + $enc = $_REQUEST[$matchall[1][0]][$passwordattr][$i]; + + } else + $enc = $args[0]; + + $enc = strtolower($enc); + + switch ($enc) { + case 'lm': + $sambapassword = new smbHash; + $vals[$i] = $sambapassword->lmhash($passwordvalue); + + break; + + case 'nt': + $sambapassword = new smbHash; + $vals[$i] = $sambapassword->nthash($passwordvalue); + + break; + + default: + $vals[$i] = password_hash($passwordvalue,$enc); + } + + $vals = array_unique($vals); + + break; + + default: + $vals = $this->get('AutoPost',$attribute,$i); + } + + if (! $vals || $vals == $attribute->getValues()) + return; + + $attribute->clearValue(); + + if (! is_array($vals)) + $attribute->setValue(array($vals)); + else + $attribute->setValue($vals); + } + + /** + * This function is invoked if we dont know which template we should be using. + * + * @return string Template ID to be used or null if the user was presented with a list. + */ + protected function getTemplateChoice() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + # First work out our template + $templates = $this->getTemplates(); + $template = $templates->getTemplate($this->template_id); + + # If the template we asked for is available + if ($this->template_id === $template->getID()) { + if (DEBUGTMP) printf('%s:%s
',__METHOD__,'Choosing the SELECTED template'); + + return $this->template_id; + + # If there are no defined templates + } elseif (count($templates->getTemplates($this->getMode(),$this->getModeContainer(),false)) <= 0) { + if (DEBUGTMP) printf('%s:%s
',__METHOD__,'Choosing the DEFAULT template, no other template applicable'); + + # Since getTemplate() returns a default template if the one we want doesnt exist, we can return $templates->getID(), it should be the default. + return $template->getID(); + + # If there is only 1 defined template, and no default available, then that is our template. + } elseif ((count($templates->getTemplates($this->getMode(),$this->getModeContainer(),true)) == 1) && ! $this->haveDefaultTemplate()) { + if (DEBUGTMP) printf('%s:%s
',__METHOD__,'AUTOMATIC choosing a template, only 1 template applicable'); + + $template = $templates->getTemplates($this->getMode(),$this->getModeContainer(),true); + $template = array_shift($template); + + # Dont render the only available template if it is invalid. + if (! $template->isInvalid()) + return $template->getID(); + else + $this->drawTemplateChoice(); + + } else { + if (DEBUGTMP) printf('%s:%s
',__METHOD__,'SELECT a template to use.'); + + # Propose the template choice + $this->drawTemplateChoice(); + } + + # If we got here, then there wasnt a template. + return null; + } + + /** DRAW ATTRIBUTE NAME **/ + + final protected function drawNameAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd.php?cmd=schema&server_id=%s&view=attributes&viewvalue=%s', + $this->getServerID(),$attribute->getName()); + + if (! $_SESSION[APPCONFIG]->getValue('appearance','show_schema_link')) + printf('%s',_($attribute->getFriendlyName())); + + elseif ($attribute->getLDAPtype()) + printf('%s', + htmlspecialchars($href), + _('Click to view the schema definition for attribute type'),$attribute->getName(false),_($attribute->getFriendlyName())); + else + printf('%s',_('This attribute is not defined in the LDAP schema'),_($attribute->getFriendlyName())); + + if (DEBUGTMPSUB) printf(' [%s]',get_class($attribute)); + } + + /** ATTRIBUTE NOTES */ + + protected function drawNotesAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $attr_note = ''; + + foreach (array('NoteAlias','NoteRequired','NoteRDN','NoteHint','NoteRO') as $note) { + $alias_note = $this->get($note,$attribute); + + if ($alias_note) { + if (trim($attr_note)) + $attr_note .= ', '; + + $attr_note .= $alias_note; + } + } + + if ($attr_note) + printf('%s',$attr_note); + } + + protected function getNoteAliasAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + # Is there a user-friendly translation available for this attribute? + $friendly_name = $attribute->getFriendlyName(); + + if (strtolower($friendly_name) != $attribute->getName()) + return sprintf('%s', + _('Note'),$friendly_name,_('is an alias for'),$attribute->getName(false),_('alias')); + else + return ''; + } + + #@todo this function shouldnt re-calculate requiredness, it should be known in the template already - need to set the ldaptype when initiating the attribute. + protected function getNoteRequiredAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $required_by = ''; + $sattr_required = ''; + + # Is this attribute required by an objectClass ? + $sattr = $this->getServer()->getSchemaAttribute($attribute->getName()); + if ($sattr) + $sattr_required = $sattr->getRequiredByObjectClasses(); + + if ($sattr_required) { + $oc = $this->template->getAttribute('objectclass'); + + if ($oc) + foreach ($oc->getValues() as $objectclass) { + # If this objectclass is in our required list + if (in_array_ignore_case($objectclass,$sattr_required)) { + $required_by .= sprintf('%s ',$objectclass); + continue; + } + + # If not, see if it is in our parent. + $sattr = $this->getServer()->getSchemaObjectClass($objectclass); + + if (array_intersect($sattr->getParents(),$sattr_required)) + $required_by .= sprintf('%s ',$objectclass); + } + + else + debug_dump_backtrace('How can there be no objectclasses?',1); + } + + if ($required_by) + return sprintf('%s',_('Required attribute for objectClass(es)'),$required_by,_('required')); + else + return ''; + } + + protected function getNoteRDNAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + # Is this attribute required because its the RDN + if ($attribute->isRDN()) + return sprintf('rdn',_('This attribute is required for the RDN.')); + else + return ''; + } + + protected function getNoteHintAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + # Is there a hint for this attribute + if ($attribute->getHint()) + return sprintf('%s',_($attribute->getHint()),_('hint')); + else + return ''; + } + + protected function getNoteROAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + # Is this attribute is readonly + if ($attribute->isReadOnly()) + return sprintf('ro',_('This attribute has been marked as Read Only.')); + else + return ''; + } + /** DRAW HIDDEN VALUES **/ + + /** + * Draw all hidden attributes + */ + final public function drawHiddenAttributes() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->template->getAttributes(true) as $attribute) + if ($attribute->hasbeenModified()) { + if ($attribute->getValues()) + foreach ($attribute->getValues() as $index => $details) + $this->draw('HiddenValue',$attribute,$index); + + # We are deleting this attribute, so we need to display an empty value + else + $this->draw('HiddenValue',$attribute,0); + } + } + + /** + * Draw specific hidden attribute + */ + final protected function drawHiddenValueAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + printf('', + htmlspecialchars($attribute->getName()),$i,htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($val)); + } + + /** DRAW DISPLAYED OLD VALUES **/ + protected function drawOldValuesAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + foreach ($attribute->getValues() as $index => $details) + $this->draw('OldValue',$attribute,$index); + } + + final protected function drawOldValueAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + echo $attribute->getOldValue($i); + } + + /** DRAW DISPLAYED CURRENT VALUES **/ + + protected function drawCurrentValuesAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + for ($i=0;$i<$attribute->getValueCount();$i++) { + if ($i > 0) + echo '
'; + + $this->draw('CurrentValue',$attribute,$i); + } + } + + /** + * Draw the current specific value of an attribute + */ + final protected function drawCurrentValueAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + if (DEBUGTMPSUB) printf(' [%s]',__METHOD__); + + echo htmlspecialchars($attribute->getValue($i)); + } + + /** + * Draw a input value for an attribute - used in a form. + */ + protected function drawFormValueAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + if (DEBUGTMPSUB) printf(' [%s]',__METHOD__); + + if ($this->getServer()->isReadOnly() || $attribute->isReadOnly() + || ($attribute->isRDN() && $this->template->getType() != 'creation' && $i < count($attribute->getValues()))) + + $this->draw('FormReadOnlyValue',$attribute,$i); + else + $this->draw('FormReadWriteValue',$attribute,$i); + + # Show the ADDVALUE DIV if the attribute can have more values, and we have rendered the last value + if ($attribute->haveMoreValues() && $attribute->getValueCount() == $i+1) + printf('
',$attribute->getName()); + + if ($attribute->getPostValue()) + printf('',$attribute->getName(),$i); + } + + protected function drawFormReadOnlyValueAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + printf('', + htmlspecialchars($attribute->getName()),$i,htmlspecialchars($attribute->getName()),$i,htmlspecialchars($val)); + } + + protected function drawFormReadWriteValueAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + if ($attribute->getHelper() || $attribute->getVerify()) + echo ''; + + } elseif ($attribute->getVerify()) + echo ''; + + if ($attribute->getVerify()) { + printf(''; + } + + if ($attribute->getHelper() || $attribute->getVerify()) + echo '
'; + + printf('', + htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($val), + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + ($attribute->getSize() > 0) ? sprintf('size="%s"',$attribute->getSize()) : '', + ($attribute->getMaxLength() > 0) ? sprintf('maxlength="%s"',$attribute->getMaxLength()) : ''); + + if ($attribute->getHelper()) { + echo ''; + $this->draw('AttributeHelper',$attribute,$i); + echo '
', + htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($attribute->getName()),$i, + ($attribute->getSize() > 0) ? sprintf('size="%s"',$attribute->getSize()) : '', + ($attribute->getMaxLength() > 0) ? sprintf('maxlength="%s"',$attribute->getMaxLength()) : ''); + + echo ''; + printf('(%s)',_('confirm')); + echo '
'; + } + + /** + * Draw specific hidden binary attribute + */ + final protected function drawHiddenValueBinaryAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + printf('', + htmlspecialchars($attribute->getName()),$i,base64_encode($val)); + } + + final protected function drawOldValueBinaryAttribute($attribute,$i) { + # If we dont have a value, we'll just return; + if (! $attribute->getOldValue($i)) + return; + + printf('[%s]',_('Binary Value')); + } + + final protected function drawCurrentValueBinaryAttribute($attribute,$i) { + printf('[%s]',_('Binary Value')); + + if (in_array($attribute->getName(),array('objectsid'))) + printf(' (%s)', binSIDtoText($attribute->getValue(0))); + } + + protected function drawFormReadOnlyValueBinaryAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->draw('CurrentValue',$attribute,$i); + echo '

'; + + $href = sprintf('download_binary_attr.php?server_id=%s&dn=%s&attr=%s&index=%s', + $this->getServerID(),rawurlencode($this->template->getDN()),$attribute->getName(),$i); + + printf('Save %s', + htmlspecialchars($href),IMGDIR,_('download value')); + + echo '
'; + } + + protected function drawFormReadWriteValueBinaryAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if ($attribute->getValue($i)) { + $this->draw('FormReadOnlyValue',$attribute,$i); + + if (! $attribute->isReadOnly() && $_SESSION[APPCONFIG]->isCommandAvailable('script','delete_attr')) + printf('Trash %s', + $attribute->getName(),$attribute->getFriendlyName(),$i,IMGDIR,_('delete attribute')); + + } else { + printf('
', + htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($attribute->getName()),$i, + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + ($attribute->getSize() > 0) ? 'size="'.$attribute->getSize().'"' : '', + ($attribute->getMaxLength() > 0) ? 'maxlength="'.$attribute->getMaxLength().'"' : ''); + } + } + + protected function drawFormReadWriteValueDateAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + echo ''; + printf(' ', + $attribute->getName(),$i, + htmlspecialchars($attribute->getName()),$i,htmlspecialchars($val), + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + ($attribute->getSize() > 0) ? sprintf('size="%s"',$attribute->getSize()) : '', + ($attribute->getMaxLength() > 0) ? sprintf('maxlength="%s"',$attribute->getMaxLength()) : ''); + + $this->draw('SelectorPopup',$attribute,$i); + echo ''."\n"; + } + + protected function drawFormReadWriteValueDnAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + if ($attribute->getHelper()) + echo '
'; + + $input_name = sprintf('new_values[%s][%s]',htmlspecialchars($attribute->getName()),$i); + $id = sprintf('new_values_%s_%s',htmlspecialchars($attribute->getName()),$i); + + printf(' ', + $input_name,$id,htmlspecialchars($val), + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + ($attribute->getSize() > 0) ? 'size="'.$attribute->getSize().'"' : '', + ($attribute->getMaxLength() > 0) ? 'maxlength="'.$attribute->getMaxLength().'"' : ''); + + # Draw a link for popping up the entry browser if this is the type of attribute that houses DNs. + draw_chooser_link('entry_form',$id,false); + echo ''; + + if ($attribute->getHelper()) { + echo ''; + $this->draw('Helper',$attribute,$i); + echo '
'; + } + + echo "\n"; + } + + protected function drawFormReadWriteValueGidAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->drawFormReadWriteValueAttribute($attribute,$i); + + $server = $this->getServer(); + $val = $attribute->getValue($i); + + # If this is a gidNumber on a non-PosixGroup entry, lookup its name and description for convenience + if ($this->template->getDN() && ! in_array_ignore_case('posixGroup',$this->getServer()->getDNAttrValue($this->template->getDN(),'objectclass'))) { + $query['filter'] = sprintf('(&(objectClass=posixGroup)(gidNumber=%s))',$val); + $query['attrs'] = array('dn','description'); + + # Reorganise our base, so that our base is first + $bases = array_unique(array_merge(array($server->getContainerTop($this->template->getDN())),$server->getBaseDN())); + + # Search our bases, until we find a match. + foreach ($bases as $base) { + $query['base'] = $base; + $group = $this->getServer()->query($query,null); + + if (count($group) > 0) { + echo '
'; + + $group = array_pop($group); + $group_dn = $group['dn']; + $group_name = explode('=',get_rdn($group_dn)); + $group_name = $group_name[1]; + $href = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s', + $this->getServerID(),rawurlencode($group_dn)); + + echo ''; + printf('%s',htmlspecialchars($href),$group_name); + + $description = isset($group['description']) ? $group['description'] : null; + + if (is_array($description)) + foreach ($description as $item) + printf(' (%s)',$item); + else + printf(' (%s)',$description); + + echo ''; + + break; + } + } + } + } + + /** + * Draw a Jpeg Attribute + */ + final protected function drawOldValueJpegAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + if (DEBUGTMPSUB) printf(' [%s]',__METHOD__); + + # If we dont have a value, we'll just return; + if (! $attribute->getOldValue($i)) + return; + + draw_jpeg_photo($this->getServer(),$this->template->getDN(),$attribute->getName(),$i,false,false); + } + + /** + * Draw a Jpeg Attribute + */ + final protected function drawCurrentValueJpegAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + if (DEBUGTMPSUB) printf(' [%s]',__METHOD__); + + # If we dont have a value, we'll just return; + if (! $attribute->getValue($i)) + return; + + # If the attribute is modified, the new value needs to be stored in a session variable for the draw_jpeg_photo callback. + if ($attribute->hasBeenModified()) { + $_SESSION['tmp'][$attribute->getName()][$i] = $attribute->getValue($i); + draw_jpeg_photo(null,$this->template->getDN(),$attribute->getName(),$i,false,false); + } else + draw_jpeg_photo($this->getServer(),$this->template->getDN(),$attribute->getName(),$i,false,false); + } + + protected function drawFormReadOnlyValueJpegAttribute($attribute,$i) { + $this->draw('HiddenValue',$attribute,$i); + $_SESSION['tmp'][$attribute->getName()][$i] = $attribute->getValue($i); + + draw_jpeg_photo(null,$this->template->getDN(),$attribute->getName(),$i,false,false); + } + + protected function drawFormReadOnlyValueMultiLineAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + printf('', + ($attribute->getRows() > 0) ? $attribute->getRows() : 5, + ($attribute->getCols() > 0) ? $attribute->getCols() : 100, + htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($attribute->getName()),$i, + $val); + } + + protected function drawFormReadWriteValueMultiLineAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + printf('', + ($attribute->getRows() > 0) ? $attribute->getRows() : 5, + ($attribute->getCols() > 0) ? $attribute->getCols() : 100, + htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($attribute->getName()),$i, + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + $val); + } + + protected function drawFormValueObjectClassAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $val = $attribute->getValue($i); + + /* It seems that openLDAP allows us to remove additional structural objectclasses + however other LDAP servers, dont remove them (even if we ask them to). */ + # Do we have our internal attributes. + $internal = $this->template->getAttribute('structuralobjectclass'); + + if ($internal) { + $structural = in_array_ignore_case($val,$internal->getValues()); + + # We'll work it out the traditional way. + } else { + # If this schema structural? + $schema_object = ($val) ? $this->getServer()->getSchemaObjectClass($val) : false; + $structural = (is_object($schema_object) && $schema_object->getType() == 'structural'); + } + + if ($structural) { + $this->draw('FormReadOnlyValue',$attribute,$i); + + printf(' (%s)', + _('This is a structural ObjectClass and cannot be removed.'), + _('structural')); + + } else + $this->draw('FormReadWriteValue',$attribute,$i); + } + + protected function getAutoPostPasswordAttribute($attribute,$i) { + # If the password is already encoded, then we'll return + if (preg_match('/^\{.+\}.+/',$attribute->getValue($i))) + return; + + $attribute->setPostValue(array('function'=>'PasswordEncrypt','args'=>sprintf('%%enc%%;%%%s%%',$attribute->getName()))); + $this->get('Post',$attribute,$i); + } + + protected function drawOldValuePasswordAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + if (DEBUGTMPSUB) printf(' [%s]',__METHOD__); + + $val = $attribute->getOldValue($i); + + if (obfuscate_password_display(get_enc_type($val))) + echo str_repeat('*',16); + else + echo nl2br(htmlspecialchars($attribute->getOldValue($i))); + } + + final protected function drawCurrentValuePasswordAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + if (DEBUGTMPSUB) printf(' [%s]',__METHOD__); + + $val = $attribute->getValue($i); + + if (obfuscate_password_display(get_enc_type($val))) + echo str_repeat('*',16); + else + echo nl2br(htmlspecialchars($attribute->getValue($i))); + } + + protected function drawFormReadOnlyValuePasswordAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $server = $this->getServer(); + $val = $attribute->getValue($i); + + if (trim($val)) + $enc_type = get_enc_type($val); + else + $enc_type = $server->getValue('appearance','password_hash'); + + $obfuscate_password = obfuscate_password_display($enc_type); + + printf('
', + ($obfuscate_password ? 'password' : 'text'), + htmlspecialchars($attribute->getName()),$i,htmlspecialchars($attribute->getName()), + $i,htmlspecialchars($val),($attribute->getSize() > 0) ? 'size="'.$attribute->getSize().'"' : ''); + + if (trim($val)) + $this->draw('CheckLink',$attribute,'new_values_'.htmlspecialchars($attribute->getName()).'_'.$i); + } + + protected function drawFormReadWriteValuePasswordAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $server = $this->getServer(); + $val = $attribute->getValue($i); + + $enc_type = get_enc_type($val); + + # Set the default hashing type if the password is blank (must be newly created) + if (trim($val)) + $enc_type = get_enc_type($val); + else + $enc_type = $server->getValue('appearance','password_hash'); + + echo '
'; + + $obfuscate_password = obfuscate_password_display($enc_type); + $id = sprintf('new_values_%s_%s',htmlspecialchars($attribute->getName()),$i); + + printf('', + ($obfuscate_password ? 'password' : 'text'), + htmlspecialchars($attribute->getName()),$i,$id, + htmlspecialchars($val), + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + ($attribute->getSize() > 0) ? sprintf('size="%s"',$attribute->getSize()) : '', + ($attribute->getMaxLength() > 0) ? sprintf('maxlength="%s"',$attribute->getMaxLength()) : ''); + + echo ''; + + if ($attribute->getHelper()) + $this->draw('Helper',$attribute,$i); + else + $this->draw('DefaultHelper',$attribute,$i); + + echo '
'; + + if ($attribute->getVerify() && $obfuscate_password) { + printf('', + htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($attribute->getName()),$i, + ($attribute->getSize() > 0) ? sprintf('size="%s"',$attribute->getSize()) : '', + ($attribute->getMaxLength() > 0) ? sprintf('maxlength="%s"',$attribute->getMaxLength()) : ''); + + echo ''; + printf('(%s)',_('confirm')); + echo '
'; + } + + $this->draw('CheckLink',$attribute,$id); + echo '
'; + } + + protected function drawFormReadWriteValueSelectionAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if ($attribute->isMultiple()) { + # For multiple selection, we draw the component only one time + if ($i > 0) + return; + + $selected = array(); + $vals = $attribute->getValues(); + $j = 0; + + if (! $vals && ! is_null($attribute->getDefault()) && ! is_array($vals = $attribute->getDefault())) + $vals = array($attribute->getDefault()); + + if (($attribute->getSize() > 0) && ($attribute->getSize() < $attribute->getOptionCount())) { + + printf(''; + + } else { + echo ''; + + foreach ($attribute->getSelection() as $value => $description) { + if (in_array($value,$vals)) + $selected[$value] = true; + + printf('', + htmlspecialchars($attribute->getName()),$j++, + htmlspecialchars($attribute->getName()),$value, + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + isset($selected[$value]) ? 'checked="checked"' : '', + $description); + } + + foreach ($vals as $val) + if (! isset($selected[$val])) + printf('', + htmlspecialchars($attribute->getName()),$j++, + htmlspecialchars($attribute->getName()),$val, + $attribute->needJS('focus') ? sprintf('onfocus="focus_%s(this);" ',$attribute->getName()) : '', + $attribute->needJS('blur') ? sprintf('onblur="blur_%s(this);" ',$attribute->getName()) : '', + $val); + + echo '
 %s
 %s
'; + } + + # This is a single value attribute + } else { + $val = $attribute->getValue($i) ? $attribute->getValue($i) : $attribute->getDefault(); + + if ($attribute->getHelper()) + echo '
'; + + $found = false; + $empty_value = false; + + # If we are a required attribute, and the selection is blank, then the user cannot submit this form. + if ($attribute->isRequired() && ! count($attribute->getSelection())) + system_message(array( + 'title'=>_('Template Value Error'), + 'body'=>sprintf('This template uses a selection list for attribute [%s], however the selection list is empty.
You may need to create some dependancy entries in your LDAP server so that this attribute renders with values. Alternatively, you may be able to define the appropriate selection values in the template file.',$attribute->getName(false)), + 'type'=>'warn')); + + printf(''; + + if ($attribute->getHelper()) { + echo '
'; + $this->draw('Helper',$attribute,$i); + echo '
'; + } + } + } + + /** + * Takes a shadow* attribute and returns the date as an integer. + * + * @param array Attribute objects + * @param string A shadow attribute name + */ + private function shadow_date($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $shadowattr = array(); + $shadowattr['lastchange'] = $this->template->getAttribute('shadowlastchange'); + $shadowattr['max'] = $this->template->getAttribute('shadowmax'); + + $shadow = array(); + $shadow['lastchange'] = $shadowattr['lastchange'] ? $shadowattr['lastchange']->getValue(0) : null; + $shadow['max'] = $shadowattr['max'] ? $shadowattr['max']->getValue(0) : null; + + if (($attribute->getName() == 'shadowlastchange') && $shadow['lastchange']) + $shadow_date = $shadow['lastchange']; + + elseif (($attribute->getName() == 'shadowmax') && ($shadow['max'] > 0) && $shadow['lastchange']) + $shadow_date = $shadow['lastchange']+$shadow['max']; + + elseif (($attribute->getName() == 'shadowwarning') && ($attribute->getValue(0) > 0) + && $shadow['lastchange'] && $shadow['max'] && $shadow['max'] > 0) + $shadow_date = $shadow['lastchange']+$shadow['max']-$attribute->getValue(0); + + elseif (($attribute->getName() == 'shadowinactive') && ($attribute->getValue(0) > 0) + && $shadow['lastchange'] && $shadow['max'] && $shadow['max'] > 0) + $shadow_date = $shadow['lastchange']+$shadow['max']+$attribute->getValue(0); + + elseif (($attribute->getName() == 'shadowmin') && ($attribute->getValue(0) > 0) && $shadow['lastchange']) + $shadow_date = $shadow['lastchange']+$attribute->getValue(0); + + elseif (($attribute->getName() == 'shadowexpire') && ($attribute->getValue(0) > 0)) + $shadow_date = $shadowattr->getValue(0); + + # Couldn't interpret the shadow date (could be 0 or -1 or something) + else + return false; + + return $shadow_date*24*3600; + } + + protected function drawShadowDateShadowAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $shadow_before_today_attrs = arrayLower($attribute->shadow_before_today_attrs); + $shadow_after_today_attrs = arrayLower($attribute->shadow_after_today_attrs); + $shadow_date = $this->shadow_date($attribute); + + if (! $shadow_date) + return; + + $today = date('U'); + + echo '
'; + if (($today < $shadow_date) && in_array(strtolower($attribute->getName()),$shadow_before_today_attrs)) + printf('(%s)', + strftime($_SESSION[APPCONFIG]->getValue('appearance','date'),$shadow_date)); + + elseif (($today > $shadow_date) && in_array(strtolower($attribute->getName()),$shadow_after_today_attrs)) + printf('(%s)', + strftime($_SESSION[APPCONFIG]->getValue('appearance','date'),$shadow_date)); + + else + printf('(%s)', + strftime($_SESSION[APPCONFIG]->getValue('appearance','date'),$shadow_date)); + + echo '
'; + } + + protected function drawFormReadOnlyValueShadowAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->drawFormReadOnlyValueAttribute($attribute,$i); + $this->draw('ShadowDate',$attribute); + } + + protected function drawFormReadWriteValueShadowAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->drawFormReadWriteValueAttribute($attribute,$i); + $this->draw('ShadowDate',$attribute); + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/PasswordAttribute.php b/lam/templates/3rdParty/pla/lib/PasswordAttribute.php new file mode 100644 index 00000000..2667197d --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/PasswordAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/Query.php b/lam/templates/3rdParty/pla/lib/Query.php new file mode 100644 index 00000000..a6941e50 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/Query.php @@ -0,0 +1,283 @@ +getServer(); + + foreach ($xmldata['query'] as $xml_key => $xml_value) { + if (DEBUG_ENABLED) + debug_log('Foreach loop Key [%s] Value [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key,is_array($xml_value)); + + switch ($xml_key) { + + # Build our attribute list from the DN and Template. + case ('attributes'): + if (DEBUG_ENABLED) + debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); + + if (is_array($xmldata['query'][$xml_key])) { + foreach ($xmldata['query'][$xml_key] as $tattrs) { + foreach ($tattrs as $index => $details) { + + if (DEBUG_ENABLED) + debug_log('Foreach tattrs Key [%s] Value [%s]',4,0,__FILE__,__LINE__,__METHOD__, + $index,$details); + + # If there is no schema definition for the attribute, it will be ignored. + if ($sattr = $server->getSchemaAttribute($index)) { + if (is_null($attribute = $this->getAttribute($sattr->getName()))) + $attribute = $this->addAttribute($sattr->getName(false),array('values'=>array())); + + $attribute->show(); + $attribute->setXML($details); + } + } + } + } + + break; + + # Build our bases list from the DN and Template. + case ('bases'): + if (isset($xmldata['query'][$xml_key]['base'])) + if (is_array($xmldata['query'][$xml_key]['base'])) + $this->base = $xmldata['query'][$xml_key]['base']; + else + $this->base = array($xmldata['query'][$xml_key]['base']); + else + error(sprintf(_('In the XML file (%s), [%s] contains an unknown key.'), + $this->filename,$xml_key),'error','index.php'); + + $this->base = array_unique($this->base); + break; + + default: + if (DEBUG_ENABLED) + debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); + + # Some key definitions need to be an array, some must not be: + $allowed_arrays = array(''); + $storelower = array(''); + $storearray = array(''); + + # Items that must be stored lowercase + if (in_array($xml_key,$storelower)) + if (is_array($xml_value)) + foreach ($xml_value as $index => $value) + $xml_value[$index] = strtolower($value); + else + $xml_value = strtolower($xml_value); + + # Items that must be stored as arrays + if (in_array($xml_key,$storearray) && ! is_array($xml_value)) + $xml_value = array($xml_value); + + # Items that should not be an array + if (! in_array($xml_key,$allowed_arrays) && is_array($xml_value)) { + debug_dump(array(__METHOD__,'key'=>$xml_key,'value'=>$xml_value)); + error(sprintf(_('In the XML file (%s), [%s] is an array, it must be a string.'), + $this->filename,$xml_key),'error'); + } + + $this->$xml_key = $xml_value; + } + } + + # Check we have some manditory items. + foreach (array() as $key) { + if (! isset($this->$key) + || (! is_array($this->$key) && ! trim($this->$key))) { + + $this->setInvalid(sprintf(_('Missing %s in the XML file.'),$key)); + break; + } + } + } + + /** + * Accept will run the query and store the results in results() + */ + public function accept() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + $query = array(); + $query['size_limit'] = get_request('size_limit','REQUEST',false,$_SESSION[APPCONFIG]->getValue('search','size_limit')); + $query['format'] = get_request('format','REQUEST',false,$_SESSION[APPCONFIG]->getValue('search','display')); + $query['orderby'] = get_request('orderby','REQUEST',false,'dn'); + + # If this is a custom search, we need to populate are paramters + if ($this->getID() == 'none') { + $bases = get_request('base','REQUEST',false,null); + $query['filter'] = get_request('filter','REQUEST',false,'objectClass=*'); + $query['scope'] = get_request('scope','REQUEST',false,'sub'); + $attrs = get_request('display_attrs','REQUEST'); + + $attrs = preg_replace('/\s+/','',$attrs); + if ($attrs) + $query['attrs'] = explode(',',$attrs); + else + $query['attrs'] = array('*'); + + } else { + $bases = $this->base; + $query['filter'] = $this->filter; + $query['scope'] = $this->scope; + $query['attrs'] = $this->getAttributeNames(); + } + + if (! $bases) + $bases = $server->getBaseDN(); + elseif (! is_array($bases)) + $bases = explode('|',$bases); + + foreach ($bases as $base) { + $query['base'] = $base; + + $time_start = utime(); + $this->results[$base] = $server->query($query,null); + $time_end = utime(); + + $this->resultsdata[$base]['time'] = round($time_end-$time_start,2); + $this->resultsdata[$base]['scope'] = $query['scope']; + $this->resultsdata[$base]['filter'] = $query['filter']; + $this->resultsdata[$base]['attrs'] = $query['attrs']; + + if ($this->getAttrSortOrder() == 'dn') + usort($this->results[$base],'pla_compare_dns'); + elseif ($this->getAttrSortOrder()) + masort($this->results[$base],$this->getAttrSortOrder()); + } + } + + /** + * This is temporary to get around objects that use a DN for rendering, for example jpegPhoto + */ + public function setDN($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->dn = $dn; + } + + /** + * This is temporary to get around objects that use a DN for rendering, for example jpegPhoto + */ + public function getDN() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->dn); + + return $this->dn; + } + + public function getDNEncode($url=true) { + // @todo Be nice to do all this in 1 location + if ($url) + return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->dn)); + else + return preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->dn); + } + + public function getAttrSortOrder() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + if (count($this->attributes)) { + masort($this->attributes,'ordersort'); + + foreach ($this->attributes as $attribute) + array_push($result,$attribute->getName()); + + } else { + $display = preg_replace('/,\s+/',',',get_request('orderby','REQUEST',false,'dn')); + + if (trim($display)) + $result = explode(',',$display); + } + + return implode(',',$result); + } + + public function getAttrDisplayOrder() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + if (count($this->attributes)) { + masort($this->attributes,'order'); + + foreach ($this->attributes as $attribute) + array_push($result,$attribute->getName()); + + } else { + $display = preg_replace('/,\s+/',',',get_request('display_attrs','REQUEST',false,'')); + + if (trim($display)) + $result = explode(',',$display); + } + + # If our display order is empty, then dynamically build it + if (! count($result)) { + foreach ($this->results as $details) + foreach ($details as $attrs) + $result = array_merge($result,array_keys(array_change_key_case($attrs))); + + $result = array_unique($result); + sort($result); + } + + # Put the DN first + array_unshift($result,'dn'); + $result = array_unique($result); + + return implode(',',$result); + } + + /** + * Test if the template is visible + * + * @return boolean + */ + public function isVisible() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->visible); + + return $this->visible; + } + + public function getDescription() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->description); + + return $this->description; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/QueryRender.php b/lam/templates/3rdParty/pla/lib/QueryRender.php new file mode 100644 index 00000000..0e6d1300 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/QueryRender.php @@ -0,0 +1,552 @@ +%s
',__METHOD__); + if (DEBUGTMP||DEBUGTMPSUB) printf('* %s [GETquery:%s]
',__METHOD__,get_request('query','REQUEST')); + if (DEBUGTMP||DEBUGTMPSUB) printf('* %s [Page:%s]
',__METHOD__,get_request('page','REQUEST')); + + $this->template_id = $this->getTemplateChoice(); + $this->page = get_request('page','REQUEST',false,1); + + # If we are the default template, make sure we pressed search + if ($this->template_id == 'none' && ! get_request('search','REQUEST')) + $this->drawTemplateChoice(); + + elseif ($this->template_id) { + $templates = $this->getTemplates(); + $this->template = $templates->getTemplate($this->template_id); + $this->template->accept(); + + $this->visitStart(); + $this->visitEnd(); + } + } + + /** + * Get our templates applicable for this object + */ + protected function getTemplates() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return new Queries($this->server_id); + } + + /** + * Are default queries enabled? + */ + protected function haveDefaultTemplate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + if ($server->getValue('query','disable_default')) + return false; + else + return true; + } + + protected function drawTemplateChoice() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $server = $this->getServer(); + + $this->drawTitle(_('Search')); + $this->drawSubTitle(); + + echo "\n"; + + $baseDNs = $server->getBaseDN(); + + printf('',JSDIR); + echo '
'; + echo '
'; + echo ''; + printf('',$server->getIndex()); + echo '
'; + + echo ''; + echo ''; + + $templates = $this->getTemplates(); + + if (count($templates->getTemplates())) { + echo ''; + printf('',_('Run a predefined query'),_('Predefined Query')); + echo ''; + echo ''; + } + + echo ''; + printf('',_('The format to show the query results'),_('Display Format')); + echo ''; + echo ''; + + echo ''; + printf('',_('Entries to show per page')); + echo ''; + echo ''; + + echo ''; + + echo ''; + echo ''; + + printf('',_('Search')); + + echo '
 
%s'; + echo ''; + echo '
%s'; + echo ''; + echo '
'; + echo '
'; + printf('
','block'); + echo '
'; + echo '
'; + printf('%s',_('Custom Query')); + echo ''; + + printf('',_('Base DN')); + printf(''; + echo ''; + + echo ''; + printf('',_('The scope in which to search'),_('Search Scope')); + + echo ''; + echo ''; + + echo ''; + printf('', + htmlspecialchars(_('Standard LDAP search filter. Example: (&(sn=Smith)(givenName=David))')),_('Search Filter')); + + printf('', + 'objectClass=*'); + + echo ''; + + echo ''; + printf('', + _('A list of attributes to display in the results (comma-separated)'),_('Show Attributes')); + + printf('', + implode(', ',$_SESSION[APPCONFIG]->getValue('search','result_attributes'))); + echo ''; + + echo ''; + printf('',_('Order by'),_('Order by')); + printf('',''); + echo ''; + + echo ''; + printf('',_('Set the search results to 0 to retrieve all available records'),_('Search Results')); + printf('',$_SESSION[APPCONFIG]->getValue('search','size_limit')); + echo ''; + + echo '
%s',count($baseDNs) == 1 ? $baseDNs[0] : ''); + + draw_chooser_link('advanced_search_form','base'); + + echo '
%s'; + echo ''; + echo '
%s
%s
%s
%s
'; + echo '
'; + echo '
'; + echo '

'; + echo '
'; + } + + private function visitStart() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->drawTitle(_('Search Results')); + $this->drawSubTitle(); + echo '
'; + } + + private function visitEnd() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $afattrs = $this->getAFAttrs(); + + # If Mass Actions Enabled + if ($_SESSION[APPCONFIG]->getValue('mass','enabled')) { + $mass_actions = array( + ' ' => '', + _('delete') => 'mass_delete', + _('edit') => 'mass_edit' + ); + } + + $this->drawBaseTabs(); + $ado = $this->template->getAttrDisplayOrder(); + $counter = 0; + $j = 0; + + foreach ($this->template->results as $base => $results) { + $counter++; + + if (! $show = get_request('show','REQUEST')) + $show = ($counter === 1 ? $this->getAjaxRef($base) : null); + + printf('
', + $this->getAjaxRef($base), ($show == $this->getAjaxRef($base) ? 'block' : 'none')); + + echo ''; + echo ''; + echo '
'; + echo '
'; + echo '
'; + + $this->drawResultsTable($base,count($results)); + + echo '
'; + echo '
'; + + switch(get_request('format','REQUEST',false,$_SESSION[APPCONFIG]->getValue('search','display'))) { + case 'list': + foreach ($results as $dndetails) { + $dndetails = array_change_key_case($dndetails); + + # Temporarily set our DN, for rendering that leverages our DN (eg: JpegPhoto) + $this->template->setDN($dndetails['dn']); + + echo ''; + + echo ''; + printf('',IMGDIR,get_icon($server->getIndex(),$dndetails['dn'])); + + printf('', + $server->getIndex(),$this->template->getDNEncode(),htmlspecialchars(get_rdn($dndetails['dn']))); + echo ''; + + printf('', + htmlspecialchars(dn_unescape($dndetails['dn']))); + + # Iterate over each attribute for this entry + foreach (explode(',',$ado) as $attr) { + # Ignore DN, we've already displayed it. + if ($attr == 'dn') + continue; + + if (! isset($dndetails[$attr])) + continue; + + # Set our object with our values + $afattrs[$attr]->clearValue(); + + if (is_array($dndetails[$attr])) + $afattrs[$attr]->initValue($dndetails[$attr]); + else + $afattrs[$attr]->initValue(array($dndetails[$attr])); + + echo ''; + echo ''; + + echo ''; + + echo ''; + echo ''; + } + + echo '
icon%s
 dn%s
 '; + $this->draw('Name',$afattrs[$attr]); + echo ''; + $this->draw('CurrentValues',$afattrs[$attr]); + echo '
'; + echo '
'; + } + + break; + + # Display the results. + case 'table': + if (! $results) { + echo _('Search returned no results'); + + continue; + } + + printf('
',$counter); + echo '
'; + printf('',$server->getIndex()); + + foreach ($this->template->resultsdata[$base]['attrs'] as $attr) + printf('',$attr); + + echo '
'; + + echo ''; + + echo ''; + echo ''; + echo ''; + echo ''; + + foreach (explode(',',$ado) as $attr) { + echo ''; + } + + echo ''; + echo ''; + + echo ''; + foreach ($results as $dndetails) { + $j++; + $dndetails = array_change_key_case($dndetails); + + # Temporarily set our DN, for rendering that leverages our DN (eg: JpegPhoto) + $this->template->setDN($dndetails['dn']); + + printf('', + $j%2 ? 'even' : 'odd',$j,$j); + + # Is mass action enabled. + if ($_SESSION[APPCONFIG]->getValue('mass','enabled')) + printf('',$j,$dndetails['dn']); + + $href = sprintf('cmd=template_engine&server_id=%s&dn=%s',$server->getIndex(),$this->template->getDNEncode()); + printf('', + htmlspecialchars($href), + IMGDIR,get_icon($server->getIndex(),$dndetails['dn'])); + + # We'll clone our attribute factory attributes, since we need to add the values to them for rendering. + foreach (explode(',',$ado) as $attr) { + # If the entry is blank, we'll draw an empty box and continue. + if (! isset($dndetails[$attr])) { + echo ''; + continue; + } + + # Special case for DNs + if ($attr == 'dn') { + $dn_display = strlen($dndetails['dn']) > 40 + ? sprintf('%s...',htmlspecialchars($dndetails['dn']),htmlspecialchars(substr($dndetails['dn'],0,40))) + : htmlspecialchars($dndetails['dn']); + + printf('',htmlspecialchars($href),$dn_display); + continue; + } + + # Set our object with our values + $afattrs[$attr]->clearValue(); + if (is_array($dndetails[$attr])) + $afattrs[$attr]->initValue($dndetails[$attr]); + else + $afattrs[$attr]->initValue(array($dndetails[$attr])); + + echo ''; + } + + echo ''; + } + + # Is mass action enabled. + if ($_SESSION[APPCONFIG]->getValue('mass','enabled')) { + printf('',++$j%2 ? 'odd' : 'even'); + printf('',$counter); + printf(''; + echo ''; + } + + echo ''; + echo '
  '; + $this->draw('Name',$afattrs[$attr]); + echo '
icon %s'; + $this->draw('CurrentValues',$afattrs[$attr]); + echo '
',2+count(explode(',',$ado))); + echo ''; + echo '
'; + echo '
'; + echo "\n\n"; + + break; + + default: + printf('Have ID [%s], run this query for page [%s]',$this->template_id,$this->page); + } + + echo '
'; + echo '
'; + echo "\n\n"; + } + + if (get_request('format','REQUEST',false,'table') == 'table') + printf('',JSDIR); + } + + public function drawSubTitle($subtitle=null) { + if (is_null($subtitle)) { + $server = $this->getServer(); + + $subtitle = sprintf('%s: %s',_('Server'),$server->getName()); + + if ($this->template) { + $subtitle .= '
'; + $subtitle .= sprintf('%s: %s',('Query'),$this->template->getID() != 'none' ? $this->template->getTitle() : _('Default')); + if ($this->template->getName()) + $subtitle .= sprintf(' (%s)',$this->template->getName(false)); + } + } + + parent::drawSubTitle($subtitle); + } + + private function getAFattrs() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attribute_factory = new AttributeFactory(); + $results = array(); + + foreach (explode(',',$this->template->getAttrDisplayOrder()) as $attr) + $results[$attr] = $attribute_factory->newAttribute($attr,array('values'=>array()),$this->getServerID()); + + return $results; + } + + private function getAjaxRef($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return preg_replace('/=/','.',base64_encode($dn)); + } + + private function drawBaseTabs() { + # Setup the Javascript to show/hide our DIVs. + echo ''; + echo "\n\n"; + + echo ''; + echo ''; + $counter = 0; + foreach ($this->template->results as $base => $results) { + if (! $show = get_request('show','REQUEST')) + $show = ($counter++ === 0 ? $this->getAjaxRef($base) : null); + + printf('', + $this->getAjaxRef($base), + $this->getAjaxRef($base), + ($show == $this->getAjaxRef($base) ? '#F0F0F0' : '#E0E0E0'), + $base); + } + echo ''; + echo '
%s
'; + echo "\n\n"; + } + + private function drawResultsTable($base,$results) { + $server = $this->getServer(); + + echo ''; + + echo ''; + printf('',_('Entries found'), + number_format($results),$this->template->resultsdata[$base]['time'],_('seconds')); + + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','export') && $_SESSION[APPCONFIG]->isCommandAvailable('script','export_form')) { + $href = htmlspecialchars(sprintf('cmd.php?cmd=export_form&server_id=%s&scope=%s&dn=%s&filter=%s&attributes=%s', + $server->getIndex(),$this->template->resultsdata[$base]['scope'], + $base,rawurlencode($this->template->resultsdata[$base]['filter']), + rawurlencode(implode(', ',$this->template->resultsdata[$base]['attrs'])))); + + printf(''; + echo ''; + echo '
%s: %s

(%s %s)
[ Save %s ]', + $href,IMGDIR,_('export results')); + } + + printf('[ rename %s:',IMGDIR,_('Format')); + + foreach (array('list','table') as $f) { + echo ' '; + + if (get_request('format','REQUEST',false,$_SESSION[APPCONFIG]->getValue('search','display')) == $f) { + printf('%s',_($f)); + + } else { + $query_string = htmlspecialchars(sprintf('%s&format=%s&show=%s&focusbase=%s',array_to_query_string($_GET,array('format','meth')),$f,$this->getAjaxRef($base),$base)); + + if (isAjaxEnabled()) + printf('%s', + $query_string,$query_string,_('Loading Search'),_($f)); + else + printf('%s',$query_string,_($f)); + } + } + + echo ' ]'; + + echo '
'; + printf('%s: %s',_('Base DN'),$base); + + echo '
'; + printf('%s: %s',_('Filter performed'),htmlspecialchars($this->template->resultsdata[$base]['filter'])); + + echo '
'; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/RandomPasswordAttribute.php b/lam/templates/3rdParty/pla/lib/RandomPasswordAttribute.php new file mode 100644 index 00000000..ca11f8db --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/RandomPasswordAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/SambaPasswordAttribute.php b/lam/templates/3rdParty/pla/lib/SambaPasswordAttribute.php new file mode 100644 index 00000000..35c1a088 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/SambaPasswordAttribute.php @@ -0,0 +1,17 @@ + diff --git a/lam/templates/3rdParty/pla/lib/SelectionAttribute.php b/lam/templates/3rdParty/pla/lib/SelectionAttribute.php new file mode 100644 index 00000000..deb1f11d --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/SelectionAttribute.php @@ -0,0 +1,71 @@ +source == 'XML' && $this->values) { + $this->selection = $this->values; + $this->values = array(); + } + + if (isset($values['type']) && $values['type'] == 'multiselect') + $this->multiple = true; + else + $this->multiple = false; + } + + public function addOption($value,$description) { + $this->selection[$value] = $description; + } + + public function addValue($new_val,$i=-1) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->addOption($new_val,$new_val); + } + + public function getOptionCount() { + return count($this->selection); + } + + public function getSelection() { + return $this->selection; + } + + public function autoValue($value) { + $this->selection = $value; + } + + public function getDefault() { + return $this->default; + } + + public function isMultiple() { + return $this->multiple; + } + + public function setMultiple() { + $this->multiple = true; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/ShadowAttribute.php b/lam/templates/3rdParty/pla/lib/ShadowAttribute.php new file mode 100644 index 00000000..eeb7cd18 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/ShadowAttribute.php @@ -0,0 +1,19 @@ + diff --git a/lam/templates/3rdParty/pla/lib/Template.php b/lam/templates/3rdParty/pla/lib/Template.php new file mode 100644 index 00000000..455baa11 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/Template.php @@ -0,0 +1,1567 @@ +noleaf = $_SESSION[APPCONFIG]->getValue('appearance','disable_default_leaf'); + } + + public function __clone() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # We need to clone our attributes, when passing back a template with getTemplate + foreach ($this->attributes as $key => $value) + $this->attributes[$key] = clone $value; + } + + /** + * Main processing to store the template. + * + * @param xmldata Parsed xmldata from xml2array object + */ + protected function storeTemplate($xmldata) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $objectclasses = array(); + + foreach ($xmldata['template'] as $xml_key => $xml_value) { + if (DEBUG_ENABLED) + debug_log('Foreach loop Key [%s] Value [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key,is_array($xml_value)); + + switch ($xml_key) { + # Build our object Classes from the DN and Template. + case ('objectclasses'): + if (DEBUG_ENABLED) + debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); + + if (isset($xmldata['template'][$xml_key]['objectclass'])) + if (is_array($xmldata['template'][$xml_key]['objectclass'])) { + foreach ($xmldata['template'][$xml_key]['objectclass'] as $index => $details) { + + # XML files with only 1 objectClass dont have a numeric index. + $soc = $server->getSchemaObjectClass(strtolower($details)); + + # If we havent recorded this objectclass already, do so now. + if (is_object($soc) && ! in_array($soc->getName(),$objectclasses)) + array_push($objectclasses,$soc->getName(false)); + + elseif (! is_object($soc) && ! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) + system_message(array( + 'title'=>_('Automatically removed objectClass from template'), + 'body'=>sprintf('%s: %s %s',$this->getTitle(),$details,_('removed from template as it is not defined in the schema')), + 'type'=>'warn')); + } + + } else { + # XML files with only 1 objectClass dont have a numeric index. + $soc = $server->getSchemaObjectClass(strtolower($xmldata['template'][$xml_key]['objectclass'])); + + # If we havent recorded this objectclass already, do so now. + if (is_object($soc) && ! in_array($soc->getName(),$objectclasses)) + array_push($objectclasses,$soc->getName(false)); + } + + break; + + # Build our attribute list from the DN and Template. + case ('attributes'): + if (DEBUG_ENABLED) + debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); + + if (is_array($xmldata['template'][$xml_key])) { + foreach ($xmldata['template'][$xml_key] as $tattrs) + foreach ($tattrs as $index => $details) { + if (DEBUG_ENABLED) + debug_log('Foreach tattrs Key [%s] Value [%s]',4,0,__FILE__,__LINE__,__METHOD__, + $index,$details); + + # If there is no schema definition for the attribute, it will be ignored. + if ($sattr = $server->getSchemaAttribute($index)) + if (is_null($this->getAttribute($sattr->getName()))) + $this->addAttribute($sattr->getName(),$details,'XML'); + } + + masort($this->attributes,'order'); + } + + break; + + default: + if (DEBUG_ENABLED) + debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); + + # Some key definitions need to be an array, some must not be: + $allowed_arrays = array('rdn'); + $storelower = array('rdn'); + $storearray = array('rdn'); + + # Items that must be stored lowercase + if (in_array($xml_key,$storelower)) + if (is_array($xml_value)) + foreach ($xml_value as $index => $value) + $xml_value[$index] = strtolower($value); + else + $xml_value = strtolower($xml_value); + + # Items that must be stored as arrays + if (in_array($xml_key,$storearray) && ! is_array($xml_value)) + $xml_value = array($xml_value); + + # Items that should not be an array + if (! in_array($xml_key,$allowed_arrays) && is_array($xml_value)) { + debug_dump(array(__METHOD__,'key'=>$xml_key,'value'=>$xml_value)); + error(sprintf(_('In the XML file (%s), [%s] is an array, it must be a string.'), + $this->filename,$xml_key),'error'); + } + + $this->$xml_key = $xml_value; + + if ($xml_key == 'invalid' && $xml_value) + $this->setInvalid(_('Disabled by XML configuration'),true); + } + } + + if (! count($objectclasses)) { + $this->setInvalid(_('ObjectClasses in XML dont exist in LDAP server.')); + return; + + } else { + $attribute = $this->addAttribute('objectClass',array('values'=>$objectclasses),'XML'); + $attribute->justModified(); + $attribute->setRequired(); + $attribute->hide(); + } + + $this->rebuildTemplateAttrs(); + + # Check we have some manditory items. + foreach (array('rdn','structural_oclass','visible') as $key) { + if (! isset($this->$key) + || (! is_array($this->$key) && ! trim($this->$key))) { + + $this->setInvalid(sprintf(_('Missing %s in the XML file.'),$key)); + break; + } + } + + # Mark our RDN attributes as RDN + $counter = 1; + foreach ($this->rdn as $key) { + if ((is_null($attribute = $this->getAttribute($key))) && (in_array_ignore_case('extensibleobject',$this->getObjectClasses()))) { + $attribute = $this->addAttribute($key,array('values'=>array())); + $attribute->show(); + } + + if (! is_null($attribute)) + $attribute->setRDN($counter++); + elseif ($this->isType('creation')) + $this->setInvalid(sprintf(_('Missing RDN attribute %s in the XML file.'),$key)); + } + } + + /** + * Is default templates enabled? + * This will disable the default template from the engine. + * + * @return boolean + */ + protected function hasDefaultTemplate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($_SESSION[APPCONFIG]->getValue('appearance','disable_default_template')) + return false; + else + return true; + } + + /** + * Return the templates of type (creation/modification) + * + * @param $string type - creation/modification + * @return array - Array of templates of that type + */ + protected function readTemplates($type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $template_xml = new Templates($this->server_id); + return $template_xml->getTemplates($type); + } + + /** + * This function will perform the following intialisation steps: + * + If a DN is set, query the ldap and load the object + * + Read our $_REQUEST variable and set the values + * After this action, the template should self describe as to whether it is an update, create + * or delete. + * (OLD values are IGNORED, we will have got them when we build this object from the LDAP server DN.) + */ + public function accept($makeVisible=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + + # If a DN is set, then query the LDAP server for the details. + if ($this->dn) { + if (! $server->dnExists($this->dn)) + system_message(array( + 'title'=>__METHOD__, + 'body'=>sprintf('DN (%s) didnt exist in LDAP?',$this->dn), + 'type'=>'info')); + + $rdnarray = rdn_explode(strtolower(get_rdn(dn_escape($this->dn)))); + + $counter = 1; + foreach ($server->getDNAttrValues($this->dn,null,LDAP_DEREF_NEVER,array_merge(array('*'),$server->getValue('server','custom_attrs'))) as $attr => $values) { + # We ignore DNs. + if ($attr == 'dn') + continue; + + $attribute = $this->getAttribute($attr); + + if (is_null($attribute)) + $attribute = $this->addAttribute($attr,array('values'=>$values)); + else + if ($attribute->getValues()) { + # Override values to those that are defined in the XML file. + if ($attribute->getSource() != 'XML') + $attribute->setValue(array_values($values)); + else + $attribute->setOldValue(array_values($values)); + + } else + $attribute->initValue(array_values($values)); + + # Work out the RDN attributes + foreach ($attribute->getValues() as $index => $value) + if (in_array(sprintf('%s=%s', + $attribute->getName(),strtolower($attribute->getValue($index))),$rdnarray)) + $attribute->setRDN($counter++); + + if ($makeVisible) + $attribute->show(); + } + + # Get the Internal Attributes + foreach ($server->getDNAttrValues($this->dn,null,LDAP_DEREF_NEVER,array_merge(array('+'),$server->getValue('server','custom_sys_attrs'))) as $attr => $values) { + $attribute = $this->getAttribute($attr); + + if (is_null($attribute)) + $attribute = $this->addAttribute($attr,array('values'=>$values)); + else + if ($attribute->getValues()) + $attribute->setValue(array_values($values)); + else + $attribute->initValue(array_values($values)); + + if (! in_array_ignore_case($attribute->getName(),$server->getValue('server','custom_attrs'))) + $attribute->setInternal(); + } + + # If this is the default template, and our $_REQUEST has defined our objectclass, then query the schema to get the attributes + } elseif ($this->container) { + if ($this->isType('default') && ! count($this->getAttributes(true)) && isset($_REQUEST['new_values']['objectclass'])) { + $attribute = $this->addAttribute('objectclass',array('values'=>$_REQUEST['new_values']['objectclass'])); + $attribute->justModified(); + $this->rebuildTemplateAttrs(); + unset($_REQUEST['new_values']['objectclass']); + } + + } elseif (get_request('create_base')) { + if (get_request('rdn')) { + $rdn = explode('=',get_request('rdn')); + $attribute = $this->addAttribute($rdn[0],array('values'=>array($rdn[1]))); + $attribute->setRDN(1); + } + + } else { + debug_dump_backtrace('No DN or CONTAINER?',1); + } + + # Read in our new values. + foreach (array('new_values') as $key) { + if (isset($_REQUEST[$key])) + foreach ($_REQUEST[$key] as $attr => $values) { + # If it isnt an array, silently ignore it. + if (! is_array($values)) + continue; + + # If _REQUEST['skip_array'] with this attr set, we'll ignore this new_value + if (isset($_REQUEST['skip_array'][$attr]) && $_REQUEST['skip_array'][$attr] == 'on') + continue; + + # Prune out entries with a blank value. + foreach ($values as $index => $value) + if (! strlen(trim($value))) + unset($values[$index]); + + $attribute = $this->getAttribute($attr); + # If the attribute is null, then no attribute exists, silently ignore it (unless this is the default template) + if (is_null($attribute) && (! $this->isType('default') && ! $this->isType(null))) + continue; + + # If it is a binary attribute, the post should have base64 encoded the value, we'll need to reverse that + if ($server->isAttrBinary($attr)) + foreach ($values as $index => $value) + $values[$index] = base64_decode($value); + + if (is_null($attribute)) { + $attribute = $this->addAttribute($attr,array('values'=>$values)); + + if (count($values)) + $attribute->justModified(); + + } else + $attribute->setValue(array_values($values)); + } + + # Read in our new binary values + if (isset($_FILES[$key]['name'])) + foreach ($_FILES[$key]['name'] as $attr => $values) { + $new_values = array(); + + foreach ($values as $index => $details) { + # Ignore empty files + if (! $_FILES[$key]['size'][$attr][$index]) + continue; + + if (! is_uploaded_file($_FILES[$key]['tmp_name'][$attr][$index])) { + if (isset($_FILES[$key]['error'][$attr][$index])) + switch($_FILES[$key]['error'][$attr][$index]) { + + # No error; possible file attack! + case 0: + $msg = _('Security error: The file being uploaded may be malicious.'); + break; + + # Uploaded file exceeds the upload_max_filesize directive in php.ini + case 1: + $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); + break; + + # Uploaded file exceeds the MAX_FILE_SIZE directive specified in the html form + case 2: + $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); + break; + + # Uploaded file was only partially uploaded + case 3: + $msg = _('The file you selected was only partially uploaded, likley due to a network error.'); + break; + + # No file was uploaded + case 4: + $msg = _('You left the attribute value blank. Please go back and try again.'); + break; + + # A default error, just in case! :) + default: + $msg = _('Security error: The file being uploaded may be malicious.'); + break; + } + + else + $msg = _('Security error: The file being uploaded may be malicious.'); + + system_message(array( + 'title'=>_('Upload Binary Attribute Error'),'body'=>$msg,'type'=>'warn')); + + } else { + $binaryfile = array(); + $binaryfile['name'] = $_FILES[$key]['tmp_name'][$attr][$index]; + $binaryfile['handle'] = fopen($binaryfile['name'],'r'); + $binaryfile['data'] = fread($binaryfile['handle'],filesize($binaryfile['name'])); + fclose($binaryfile['handle']); + + $new_values[$index] = $binaryfile['data']; + } + } + + if (count($new_values)) { + $attribute = $this->getAttribute($attr); + + if (is_null($attribute)) + $attribute = $this->addAttribute($attr,array('values'=>$new_values)); + else + foreach ($new_values as $value) + $attribute->addValue($value); + + $attribute->justModified(); + } + } + } + + # If there are any single item additions (from the add_attr form for example) + if (isset($_REQUEST['single_item_attr'])) { + if (isset($_REQUEST['single_item_value'])) { + if (! is_array($_REQUEST['single_item_value'])) + $values = array($_REQUEST['single_item_value']); + else + $values = $_REQUEST['single_item_value']; + + } elseif (isset($_REQUEST['binary'])) { + /* Special case for binary attributes (like jpegPhoto and userCertificate): + * we must go read the data from the file and override $_REQUEST['single_item_value'] with the + * binary data. Secondly, we must check if the ";binary" option has to be appended to the name + * of the attribute. */ + + if ($_FILES['single_item_value']['size'] === 0) + system_message(array( + 'title'=>_('Upload Binary Attribute Error'), + 'body'=>sprintf('%s %s',_('The file you chose is either empty or does not exist.'),_('Please go back and try again.')), + 'type'=>'warn')); + + else { + if (! is_uploaded_file($_FILES['single_item_value']['tmp_name'])) { + if (isset($_FILES['single_item_value']['error'])) + switch($_FILES['single_item_value']['error']) { + + # No error; possible file attack! + case 0: + $msg = _('Security error: The file being uploaded may be malicious.'); + break; + + # Uploaded file exceeds the upload_max_filesize directive in php.ini + case 1: + $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); + break; + + # Uploaded file exceeds the MAX_FILE_SIZE directive specified in the html form + case 2: + $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); + break; + + # Uploaded file was only partially uploaded + case 3: + $msg = _('The file you selected was only partially uploaded, likley due to a network error.'); + break; + + # No file was uploaded + case 4: + $msg = _('You left the attribute value blank. Please go back and try again.'); + break; + + # A default error, just in case! :) + default: + $msg = _('Security error: The file being uploaded may be malicious.'); + break; + } + + else + $msg = _('Security error: The file being uploaded may be malicious.'); + + system_message(array( + 'title'=>_('Upload Binary Attribute Error'),'body'=>$msg,'type'=>'warn'),'index.php'); + } + + $binaryfile = array(); + $binaryfile['name'] = $_FILES['single_item_value']['tmp_name']; + $binaryfile['handle'] = fopen($binaryfile['name'],'r'); + $binaryfile['data'] = fread($binaryfile['handle'],filesize($binaryfile['name'])); + fclose($binaryfile['handle']); + + $values = array($binaryfile['data']); + } + } + + if (count($values)) { + $attribute = $this->getAttribute($_REQUEST['single_item_attr']); + + if (is_null($attribute)) + $attribute = $this->addAttribute($_REQUEST['single_item_attr'],array('values'=>$values)); + else + $attribute->setValue(array_values($values)); + + $attribute->justModified(); + } + } + + # If this is the default creation template, we need to set some additional values + if ($this->isType('default') && $this->getContext() == 'create') { + # Load our schema, based on the objectclasses that may have already been defined. + if (! get_request('create_base')) + $this->rebuildTemplateAttrs(); + + # Set the RDN attribute + $counter = 1; + foreach (get_request('rdn_attribute','REQUEST',false,array()) as $key => $value) { + $attribute = $this->getAttribute($value); + + if (! is_null($attribute)) + $attribute->setRDN($counter++); + + else { + system_message(array( + 'title'=>_('No RDN attribute'), + 'body'=>_('No RDN attribute was selected'), + 'type'=>'warn'),'index.php'); + + die(); + } + } + } + } + + /** + * Set the DN for this template, if we are editing entries + * + * @param dn The DN of the entry + */ + public function setDN($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($this->container)) + system_message(array( + 'title'=>__METHOD__, + 'body'=>'CONTAINER set while setting DN', + 'type'=>'info')); + + $this->dn = $dn; + } + + /** + * Set the RDN attributes + * Given an RDN, mark the attributes as RDN attributes. If there is no defined attribute, + * then the remaining RDNs will be returned. + * + * @param RDN + * @return RDN attributes not processed + */ + public function setRDNAttributes($rdn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Setup to work out our RDN. + $rdnarray = rdn_explode($rdn); + + $counter = 1; + foreach ($this->getAttributes(true) as $attribute) + foreach ($rdnarray as $index => $rdnattr) { + list($attr,$value) = explode('=',$rdnattr); + + if (strtolower($attr) == $attribute->getName()) { + $attribute->setRDN($counter++); + unset($rdnarray[$index]); + } + } + + return $rdnarray; + } + + /** + * Display the DN for this template entry. If the DN is not set (creating a new entry), then + * a generated DN will be produced, taken from the RDN and the CONTAINER details. + * + * @return dn + */ + public function getDN() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->dn); + + if ($this->dn) + return $this->dn; + + # If DN is not set, our DN will be made from our RDN and Container. + elseif ($this->getRDN() && $this->getContainer()) + return sprintf('%s,%s',$this->getRDN(),$this->GetContainer()); + + # If container is not set, we're probably creating the base + elseif ($this->getRDN() && get_request('create_base')) + return $this->getRDN(); + } + + public function getDNEncode($url=true) { + // @todo Be nice to do all this in 1 location + if ($url) + return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->getDN())); + else + return preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->getDN()); + } + + /** + * Set the container for this template, if we are creating entries + * + * @param dn The DN of the container + * @todo Trigger a query to the LDAP server and generate an error if the container doesnt exist + */ + public function setContainer($container) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($this->dn)) + system_message(array( + 'title'=>__METHOD__, + 'body'=>'DN set while setting CONTAINER', + 'type'=>'info')); + + $this->container = $container; + } + + /** + * Get the DN of the container for this entry + * + * @return dn DN of the container + */ + public function getContainer() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->container); + + return $this->container; + } + + public function getContainerEncode($url=true) { + // @todo Be nice to do all this in 1 location + if ($url) + return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->container)); + else + return preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->container); + } + + /** + * Copy a DN + */ + public function copy($template,$rdn,$asnew=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $rdnarray = rdn_explode($rdn); + + $counter = 1; + foreach ($template->getAttributes(true) as $sattribute) { + $attribute = $this->addAttribute($sattribute->getName(false),array('values'=>$sattribute->getValues())); + + # Set our new RDN, and its values + if (is_null($attribute)) { + debug_dump_backtrace('Attribute is null, it probably doesnt exist in the destination server?'); + + } else { + + # Mark our internal attributes. + if ($sattribute->isInternal()) + $attribute->setInternal(); + + $modified = false; + foreach ($rdnarray as $index => $rdnattr) { + list($attr,$value) = explode('=',$rdnattr); + if (strtolower($attr) == $attribute->getName()) { + + # If this is already marked as an RDN, then this multivalue RDN was updated on a previous loop + if (! $modified) { + $attribute->setValue(array($value)); + $attribute->setRDN($counter++); + $modified = true; + + } else { + $attribute->addValue($value); + } + + # This attribute has been taken care of, we'll drop it from our list. + unset($rdnarray[$index]); + } + } + } + + // @todo If this is a Jpeg Attribute, we need to mark it read only, since it cant be deleted like text attributes can + if (strcasecmp(get_class($attribute),'jpegAttribute') == 0) + $attribute->setReadOnly(); + } + + # If we have any RDN values left over, there werent in the original entry and need to be added. + foreach ($rdnarray as $rdnattr) { + list($attr,$value) = explode('=',$rdnattr); + + $attribute = $this->addAttribute($attr,array('values'=>array($value))); + + if (is_null($attribute)) + debug_dump_backtrace('Attribute is null, it probably doesnt exist in the destination server?'); + else + $attribute->setRDN($counter++); + } + + # If we are copying into a new entry, we need to discard all the "old values" + if ($asnew) + foreach ($this->getAttributes(true) as $sattribute) + $sattribute->setOldValue(array()); + } + + /** + * Get Attributes by LDAP type + * This function will return a list of attributes by LDAP type (MUST,MAY). + * + * @return array Array of attributes. + */ + function getAttrbyLdapType($type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + foreach ($this->attributes as $index => $attribute) { + if ($attribute->getLDAPtype() == strtolower($type)) + array_push($result,$attribute->getName()); + } + + return $result; + } + + /** + * Return true if this is a MUST,MAY attribute + */ + function isAttrType($attr,$type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (in_array(strtolower($attr),$this->getAttrbyLdapType($type))) + return true; + else + return false; + } + + /** + * Return the attributes that comprise the RDN. + * + * @return array Array of RDN objects + */ + private function getRDNObjects() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $return = array(); + + foreach ($this->attributes as $attribute) + if ($attribute->isRDN()) + array_push($return,$attribute); + + masort($return,'rdn'); + return $return; + } + + /** + * Get all the RDNs for this template, in RDN order. + * + * @return array RDNs in order. + */ + public function getRDNAttrs() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $return = array(); + + foreach ($this->getRDNObjects() as $attribute) { + # We'll test if two RDN's have the same number (we cant test anywhere else) + if (isset($return[$attribute->isRDN()]) && $this->getType() == 'creation') + system_message(array( + 'title'=>_('RDN attribute sequence already defined'), + 'body'=>sprintf('%s %s', + sprintf(_('There is a problem in template [%s].'),$this->getName()), + sprintf(_('RDN attribute sequence [%s] is already used by attribute [%s] and cant be used by attribute [%s] also.'), + $attribute->isRDN(),$return[$attribute->isRDN()],$attribute->getName())), + 'type'=>'error'),'index.php'); + + $return[$attribute->isRDN()] = $attribute->getName(); + } + + return $return; + } + + /** + * Return the RDN for this template. If the DN is already defined, then the RDN will be calculated from it. + * If the DN is not set, then the RDN will be calcuated from the template attribute definitions + * + * @return rdn RDN for this template + */ + public function getRDN() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # If the DN is set, then the RDN will be calculated from it. + if ($this->dn) + return get_rdn($this->dn); + + $rdn = ''; + + foreach ($this->getRDNObjects() as $attribute) { + $vals = $attribute->getValues(); + + # If an RDN attribute has no values, return with an empty string. The calling script should handle this. + if (! count($vals)) + return ''; + + foreach ($vals as $val) + $rdn .= sprintf('%s=%s+',$attribute->getName(false),$val); + } + + # Chop the last plus sign off when returning + return preg_replace('/\+$/','',$rdn); + } + + /** + * Return the attribute name part of the RDN + */ + public function getRDNAttributeName() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attr = array(); + + if ($this->getDN()) { + $i = strpos($this->getDN(),','); + if ($i !== false) { + $attrs = explode('\+',substr($this->getDN(),0,$i)); + foreach ($attrs as $id => $attr) { + list ($name,$value) = explode('=',$attr); + $attrs[$id] = $name; + } + + $attr = array_unique($attrs); + } + } + + return $attr; + } + + /** + * Determine the type of template this is + */ + public function getContext() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->getContainer() && get_request('cmd','REQUEST') == 'copy') + return 'copyasnew'; + elseif ($this->getContainer() || get_request('create_base')) + return 'create'; + elseif ($this->getDN()) + return 'edit'; + else + return 'unknown'; + } + + /** + * Test if the template is visible + * + * @return boolean + */ + public function isVisible() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->visible); + + return $this->visible; + } + + public function setVisible() { + $this->visible = true; + } + + public function setInvisible() { + $this->visible = false; + } + + public function getRegExp() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->regexp); + + return $this->regexp; + } + + /** + * Test if this template has been marked as a read-only template + */ + public function isReadOnly() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ((($this->getContext() == 'edit') && $this->readonly) || $this->getServer()->isReadOnly()) + return true; + else + return false; + } + + /** + * Get the attribute entries + * + * @param boolean Include the optional attributes + * @return array Array of attributes + */ + public function getAttributes($optional=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($optional) + return $this->attributes; + + $result = array(); + foreach ($this->attributes as $attribute) { + if (! $attribute->isRequired()) + continue; + + array_push($result,$attribute); + } + + return $result; + } + + /** + * Return a list of attributes that should be shown + */ + public function getAttributesShown() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + foreach ($this->attributes as $attribute) + if ($attribute->isVisible()) + array_push($result,$attribute); + + return $result; + } + + /** + * Return a list of the internal attributes + */ + public function getAttributesInternal() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + foreach ($this->attributes as $attribute) + if ($attribute->isInternal()) + array_push($result,$attribute); + + return $result; + } + + /** + * Return the objectclasses defined in this template + * + * @return array Array of Objects + */ + public function getObjectClasses() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attribute = $this->getAttribute('objectclass'); + if ($attribute) + return $attribute->getValues(); + else + return array(); + } + + /** + * Get template icon + */ + public function getIcon() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->icon); + + return isset($this->icon) ? sprintf('%s/%s',IMGDIR,$this->icon) : ''; + } + + /** + * Return the template description + * + * @return string Description + */ + public function getDescription() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->description); + + return $this->description; + } + + /** + * Set a template as invalid + * + * @param string Message indicating the reason the template has been invalidated + */ + public function setInvalid($msg,$admin=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->invalid = true; + $this->invalid_reason = $msg; + $this->invalid_admin = $admin; + } + + /** + * Get the template validity or the reason it is invalid + * + * @return string Invalid reason, or false if not invalid + */ + public function isInValid() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->invalid) + return $this->invalid_reason; + else + return false; + } + + public function isAdminDisabled() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->invalid_admin); + + return $this->invalid_admin; + } + + /** + * Set the minimum number of values for an attribute + * + * @param object Attribute + * @param int + */ + private function setMinValueCount($attr,$value) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attribute = $this->getAttribute($attr); + + if (! is_null($attribute)) + $attribute->setMinValueCount($value); + } + + /** + * Set the LDAP type property for an attribute + * + * @param object Attribute + * @param string (MUST,MAY,OPTIONAL) + */ + private function setAttrLDAPtype($attr,$value) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attribute = $this->getAttribute($attr); + + if (is_null($attribute)) + $attribute = $this->addAttribute($attr,array('values'=>array())); + + $attribute->setLDAPtype($value); + } + + /** + * OnChangeAdd javascript processing + */ + public function OnChangeAdd($origin,$value) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attribute = $this->getAttribute($origin); + + if (preg_match('/^=(\w+)\((.*)\)$/',$value,$matches)) { + $command = $matches[1]; + $arg = $matches[2]; + } else + return; + + switch ($command) { + /* + autoFill:string + string is a literal string, and may contain many fields like %attr|start-end/flags% + to substitute values read from other fields. + |start-end is optional, but must be present if the k flag is used. + /flags is optional. + + flags may be: + T: Read display text from selection item (drop-down list), otherwise, read the value of the field + For fields that aren't selection items, /T shouldn't be used, and the field value will always be read. + k: Tokenize: + If the "k" flag is not given: + A |start-end instruction will perform a sub-string operation upon + the value of the attr, passing character positions start-end through. + start can be 0 for first character, or any other integer. + end can be 0 for last character, or any other integer for a specific position. + If the "k" flag is given: + The string read will be split into fields, using : as a delimiter + "start" indicates which field number to pass through. + K: The string read will be split into fields, using ' ' as a delimiter "start" indicates which field number to pass through. + l: Make the result lower case. + U: Make the result upper case. + */ + case 'autoFill': + if (! preg_match('/;/',$arg)) { + system_message(array( + 'title'=>_('Problem with autoFill() in template'), + 'body'=>sprintf('%s (%s)',_('There is only 1 argument, when there should be two'),$attribute->getName(false)), + 'type'=>'warn')); + + return; + } + + list($attr,$string) = preg_split('(([^,]+);(.*))',$arg,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + preg_match_all('/%(\w+)(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%/U',$string,$matchall); + //print"
";print_r($matchall); //0 = highlevel match, 1 = attr, 2 = subst, 3 = mod
+
+				if (! isset($attribute->js['autoFill']))
+					$attribute->js['autoFill'] = '';
+
+				$formula = $string;
+				$formula = preg_replace('/^([^%])/','\'$1',$formula);
+				$formula = preg_replace('/([^%])$/','$1\'',$formula);
+
+				# Check that our attributes match our schema attributes.
+				foreach ($matchall[1] as $index => $checkattr) {
+					$sattr = $this->getServer()->getSchemaAttribute($checkattr);
+
+					# If the attribute is the same as in the XML file, then dont need to do anything.
+					if (! $sattr || ! strcasecmp($sattr->getName(),$checkattr))
+						continue;
+
+					$formula = preg_replace("/$checkattr/",$sattr->getName(),$formula);
+					$matchall[1][$index] = $sattr->getName();
+				}
+
+				$elem_id = 0;
+
+				foreach ($matchall[0] as $index => $null) {
+					$match_attr = strtolower($matchall[1][$index]);
+					$match_subst = $matchall[2][$index];
+					$match_mod = $matchall[3][$index];
+
+					$substrarray = array();
+
+					if (! isset($varcount[$match_attr]))
+						$varcount[$match_attr] = 0;
+					else
+						$varcount[$match_attr]++;
+
+					$js_match_attr = $match_attr;
+					$match_attr = $js_match_attr.'xx'.$varcount[$match_attr];
+
+					$formula = preg_replace('/%'.$js_match_attr.'([|\/%])/i','%'.$match_attr.'$1',$formula,1);
+
+					$attribute->js['autoFill'] .= sprintf("  var %s;\n",$match_attr);
+					$attribute->js['autoFill'] .= sprintf(
+							"  var elem$elem_id = document.getElementById(pre+'%s'+suf);\n".
+							"  if (!elem$elem_id) return;\n", $js_match_attr);
+
+					if (strstr($match_mod,'T')) {
+						$attribute->js['autoFill'] .= sprintf("  %s = elem$elem_id.options[elem$elem_id.selectedIndex].text;\n",
+							$match_attr);
+					} else {
+						$attribute->js['autoFill'] .= sprintf("  %s = elem$elem_id.value;\n",$match_attr);
+					}
+
+					$elem_id++;
+
+					if (strstr($match_mod,'k')) {
+						preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray);
+						if (isset($substrarray[1][0])) {
+							$tok_idx = $substrarray[1][0];
+						} else {
+							$tok_idx = '0';
+						}
+						$attribute->js['autoFill'] .= sprintf("   %s = %s.split(':')[%s];\n",$match_attr,$match_attr,$tok_idx);
+
+					} elseif (strstr($match_mod,'K')) {
+						preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray); 
+						if (isset($substrarray[1][0])) { 
+							$tok_idx = $substrarray[1][0]; 
+						} else { 
+							$tok_idx = '0'; 
+						} 
+						$attribute->js['autoFill'] .= sprintf("   %s = %s.split(' ')[%s];\n",$match_attr,$match_attr,$tok_idx); 
+
+					} else {
+						preg_match_all('/([0-9]*)-([0-9]*)/',trim($match_subst),$substrarray);
+						if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) {
+							$attribute->js['autoFill'] .= sprintf("   %s = %s.substr(%s,%s);\n",
+								$match_attr,$match_attr,
+								$substrarray[1][0] ? $substrarray[1][0] : '0',
+								$substrarray[2][0] ? $substrarray[2][0] : sprintf('%s.length',$match_attr));
+						}
+					}
+
+					if (strstr($match_mod,'l')) {
+						$attribute->js['autoFill'] .= sprintf("   %s = %s.toLowerCase();\n",$match_attr,$match_attr);
+					}
+					if (strstr($match_mod,'U')) {
+						$attribute->js['autoFill'] .= sprintf("   %s = %s.toUpperCase();\n",$match_attr,$match_attr);
+					}
+					if (strstr($match_mod,'A')) {
+						$attribute->js['autoFill'] .= sprintf("   %s = toAscii(%s);\n",$match_attr,$match_attr);
+					}
+
+					# Matchfor only entry without modifiers.
+					$formula = preg_replace('/^%('.$match_attr.')%$/U','$1 + \'\'',$formula);
+					# Matchfor only entry with modifiers.
+					$formula = preg_replace('/^%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%$/U','$1 + \'\'',$formula);
+					# Matchfor begining entry.
+					$formula = preg_replace('/^%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%/U','$1 + \'',$formula);
+					# Matchfor ending entry.
+					$formula = preg_replace('/%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?%$/U','\' + $1 ',$formula);
+					# Match for entries not at begin/end.
+					$formula = preg_replace('/%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[:lTUA]+)?%/U','\' + $1 + \'',$formula);
+					$attribute->js['autoFill'] .= "\n";
+				}
+
+				$attribute->js['autoFill'] .= sprintf(" fillRec(pre+'%s'+suf, %s); // %s\n",strtolower($attr),$formula,$string);
+				$attribute->js['autoFill'] .= "\n";
+				break;
+
+			default: $return = '';
+		}
+	}
+
+	/**
+	 * This functions main purpose is to discover our MUST attributes based on objectclass
+	 * definitions in the template file and to discover which of the objectclasses are
+	 * STRUCTURAL - without one, creating an entry will just product an LDAP error.
+	 */
+	private function rebuildTemplateAttrs() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$server = $this->getServer();
+
+		# Collect our structural, MUST & MAY attributes.
+		$oclass_processed = array();
+		$superclasslist = array();
+		$allattrs = array('objectclass');
+
+		foreach ($this->getObjectClasses() as $oclass) {
+			# If we get some superclasses - then we'll need to go through them too.
+			$supclass = true;
+			$inherited = false;
+
+			while ($supclass) {
+				$soc = $server->getSchemaObjectClass($oclass);
+
+				if ($soc->getType() == 'structural' && (! $inherited))
+					array_push($this->structural_oclass,$oclass);
+
+				# Make sure our MUST attributes are marked as such for this template.
+				if ($soc->getMustAttrs())
+					foreach ($soc->getMustAttrs() as $index => $details) {
+						$objectclassattr = $details->getName();
+
+						# We add the 'objectClass' attribute, only if it's explicitly in the template attribute list
+						if ((strcasecmp('objectClass',$objectclassattr) != 0) ||
+								((strcasecmp('objectClass',$objectclassattr) == 0) && (! is_null($this->getAttribute($objectclassattr))))) {
+
+							# Go through the aliases, and ignore any that are already defined.
+							$ignore = false;
+							$sattr = $server->getSchemaAttribute($objectclassattr);
+							foreach ($sattr->getAliases() as $alias) {
+								if ($this->isAttrType($alias,'must')) {
+									$ignore = true;
+									break;
+								}
+							}
+
+							if ($ignore)
+								continue;
+
+							$this->setAttrLDAPtype($sattr->getName(),'must');
+							$this->setMinValueCount($sattr->getName(),1);
+
+							# We need to mark the attributes as show, except for the objectclass attribute.
+							if (strcasecmp('objectClass',$objectclassattr) != 0) {
+								$attribute = $this->getAttribute($sattr->getName());
+								$attribute->show();
+							}
+						}
+
+						if (! in_array($objectclassattr,$allattrs))
+							array_push($allattrs,$objectclassattr);
+					}
+
+				if ($soc->getMayAttrs())
+					foreach ($soc->getMayAttrs() as $index => $details) {
+						$objectclassattr = $details->getName();
+						$sattr = $server->getSchemaAttribute($objectclassattr);
+
+						# If it is a MUST attribute, skip to the next one.
+						if ($this->isAttrType($objectclassattr,'must'))
+							continue;
+
+						if (! $this->isAttrType($objectclassattr,'may'))
+							$this->setAttrLDAPtype($sattr->getName(false),'optional');
+
+						if (! in_array($objectclassattr,$allattrs))
+							array_push($allattrs,$objectclassattr);
+					}
+
+				# Keep a list to objectclasses we have processed, so we dont get into a loop.
+				array_push($oclass_processed,$oclass);
+				$supoclasses = $soc->getSupClasses();
+
+				if (count($supoclasses) || count($superclasslist)) {
+					foreach ($supoclasses as $supoclass) {
+						if (! in_array($supoclass,$oclass_processed))
+							$superclasslist[] = $supoclass;
+					}
+
+					$oclass = array_shift($superclasslist);
+					if ($oclass)
+						$inherited = true;
+					else
+						$supclass = false;
+
+				} else {
+					$supclass = false;
+				}
+			}
+		}
+
+		# Check that attributes are defined by an ObjectClass
+		foreach ($this->getAttributes(true) as $index => $attribute)
+			if (! in_array($attribute->getName(),$allattrs) && (! array_intersect($attribute->getAliases(),$allattrs))
+				&& (! in_array_ignore_case('extensibleobject',$this->getObjectClasses()))
+				&& (! in_array_ignore_case($attribute->getName(),$server->getValue('server','custom_attrs')))) {
+				unset($this->attributes[$index]);
+
+				if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning'))
+					system_message(array(
+						'title'=>_('Automatically removed attribute from template'),
+						'body'=>sprintf('%s: %s %s',$this->getTitle(),$attribute->getName(false),_('removed from template as it is not defined by an ObjectClass')),
+						'type'=>'warn'));
+			}
+	}
+
+	/**
+	 * Return an array, that can be passed to ldap_add().
+	 * Attributes with empty values will be excluded.
+	 */
+	public function getLDAPadd($attrsOnly=false) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$return = array();
+		$returnattrs = array();
+
+		if ($attrsOnly && count($returnattrs))
+			return $returnattrs;
+
+		foreach ($this->getAttributes(true) as $attribute)
+			if (! $attribute->isInternal() && count($attribute->getValues())) {
+				$return[$attribute->getName()] = $attribute->getValues();
+				$returnattrs[$attribute->getName()] = $attribute;
+			}
+
+		# Ensure that our objectclasses has "top".
+		if (isset($return['objectclass']) && ! in_array('top',$return['objectclass']))
+			array_push($return['objectclass'],'top');
+
+		if ($attrsOnly)
+			return $returnattrs;
+
+		return $return;
+	}
+
+	/**
+	 * Return an array, that can be passed to ldap_mod_replace().
+	 * Only attributes that have changed their value will be returned.
+	 *
+	 * This function will cache its results, so that it can be called with count() to see
+	 * if there are changes, and if they are, the 2nd call will just return the results
+	 *
+	 * @param boolean Return the attribute objects (useful for a confirmation process), or the modification array for ldap_modify()
+	 */
+	public function getLDAPmodify($attrsOnly=false,$index=0) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		static $return = array();
+		static $returnattrs = array();
+
+		if ($attrsOnly && isset($returnattrs[$index]) && count($returnattrs[$index]))
+			return $returnattrs[$index];
+
+		$returnattrs[$index] = array();
+		$return[$index] = array();
+
+		# If an objectclass is being modified, we need to remove all the orphan attributes that would result.
+		if ($this->getAttribute('objectclass')->hasBeenModified()) {
+			$attr_to_keep = array();
+			$server = $this->getServer();
+
+			# Make sure that there will be a structural object class remaining.
+			$haveStructural = false;
+			foreach ($this->getAttribute('objectclass')->getValues() as $value) {
+				$soc = $server->getSchemaObjectClass($value);
+
+				if ($soc) {
+					if ($soc->isStructural())
+						$haveStructural = true;
+
+					# While we are looping, workout which attributes these objectclasses define.
+					foreach ($soc->getMustAttrs(true) as $value)
+						if (! in_array($value->getName(),$attr_to_keep))
+							array_push($attr_to_keep,$value->getName());
+
+					foreach ($soc->getMayAttrs(true) as $value)
+						if (! in_array($value->getName(),$attr_to_keep))
+							array_push($attr_to_keep,$value->getName());
+				}
+			}
+
+			if (! $haveStructural)
+				error(_('An entry should have one structural objectClass.'),'error','index.php');
+
+			# Work out the attributes to delete.
+			foreach ($this->getAttribute('objectclass')->getRemovedValues() as $value) {
+				$soc = $server->getSchemaObjectClass($value);
+
+				foreach ($soc->getMustAttrs() as $value) {
+					$attribute = $this->getAttribute($value->getName());
+
+					if ($attribute && (! in_array($value->getName(),$attr_to_keep)) && ($value->getName() != 'objectclass'))
+						#array_push($attr_to_delete,$value->getName(false));
+						$attribute->setForceDelete();
+				}
+
+				foreach ($soc->getMayAttrs() as $value) {
+					$attribute = $this->getAttribute($value->getName());
+
+					if ($attribute && (! in_array($value->getName(),$attr_to_keep)) && ($value->getName() != 'objectclass'))
+						$attribute->setForceDelete();
+				}
+			}
+		}
+
+		foreach ($this->getAttributes(true) as $attribute)
+			if ($attribute->hasBeenModified()
+				&& (count(array_diff($attribute->getValues(),$attribute->getOldValues())) || ! count($attribute->getValues())
+					|| $attribute->isForceDelete() || (count($attribute->getValues()) != count($attribute->getOldValues()))))
+				$returnattrs[$index][$attribute->getName()] = $attribute;
+
+		if ($attrsOnly)
+			return $returnattrs[$index];
+
+		foreach ($returnattrs[$index] as $attribute)
+			$return[$index][$attribute->getName()] = $attribute->getValues();
+
+		return $return[$index];
+	}
+
+	/**
+	 * Get the attributes that are marked as force delete
+	 * We'll cache this result in the event of multiple calls.
+	 */
+	public function getForceDeleteAttrs() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		static $result = array();
+
+		if (count($result))
+			return $result;
+
+		foreach ($this->attributes as $attribute)
+			if ($attribute->isForceDelete())
+				array_push($result,$attribute);
+
+		return $result;
+	}
+
+	/**
+	 * Get available attributes
+	 */
+	public function getAvailAttrs() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$attributes = array();
+		$server = $this->getServer();
+
+		# Initialise the Attribute Factory.
+		$attribute_factory = new AttributeFactory();
+
+		if (in_array_ignore_case('extensibleobject',$this->getObjectClasses())) {
+			foreach ($server->SchemaAttributes() as $sattr) {
+				$attribute = $attribute_factory->newAttribute($sattr->getName(),array('values'=>array()),$server->getIndex(),null);
+				array_push($attributes,$attribute);
+			}
+
+		} else {
+			$attrs = array();
+
+			foreach ($this->getObjectClasses() as $oc) {
+				$soc = $server->getSchemaObjectClass($oc);
+				$attrs = array_merge($attrs,$soc->getMustAttrNames(true),$soc->getMayAttrNames(true));
+				$attrs = array_unique($attrs);
+			}
+
+			foreach ($attrs as $attr)
+				if (is_null($this->getAttribute($attr))) {
+					$attribute = $attribute_factory->newAttribute($attr,array('values'=>array()),$server->getIndex(),null);
+					array_push($attributes,$attribute);
+				}
+		}
+
+		masort($attributes,'name');
+		return $attributes;
+	}
+
+	public function isNoLeaf() {
+		return $this->noleaf;
+	}
+}
+?>
diff --git a/lam/templates/3rdParty/pla/lib/TemplateRender.php b/lam/templates/3rdParty/pla/lib/TemplateRender.php
new file mode 100644
index 00000000..06a38657
--- /dev/null
+++ b/lam/templates/3rdParty/pla/lib/TemplateRender.php
@@ -0,0 +1,2515 @@
+%s:%s
',time(),__METHOD__); + if (DEBUGTMP||DEBUGTMPSUB) printf('* %s [Visit-Start:%s]
',__METHOD__,get_class($this)); + + $tree = get_cached_item($this->server_id,'tree'); + if (! $tree) + $tree = Tree::getInstance($this->server_id); + + $treeitem = $tree->getEntry($this->dn); + + # If we have a DN, and no template_id, see if the tree has one from last time + if ($this->dn && is_null($this->template_id) && $treeitem && $treeitem->getTemplate()) + $this->template_id = $treeitem->getTemplate(); + + # Check that we have a valid template, or present a selection + # @todo change this so that the modification templates rendered are the ones for the objectclass of the dn. + if (! $this->template_id) + $this->template_id = $this->getTemplateChoice(); + + if ($treeitem) + $treeitem->setTemplate($this->template_id); + + $this->page = get_request('page','REQUEST',false,1); + + if ($this->template_id) { + if (! $this->template) + parent::accept(); + + $this->url_base = sprintf('server_id=%s&dn=%s', + $this->getServerID(),$this->template->getDNEncode()); + $this->layout['hint'] = sprintf('%s%%s', + IMGDIR,_('Hint')); + $this->layout['action'] = '%s%s'; + $this->layout['actionajax'] = '%s%s'; + + # If we dont want to render this template automatically, we'll return here. + if ($norender) + return; + + $this->visitStart(); + + foreach ($this->template->getAttributes(true) as $attribute) { + # Evaluate our defaults + if ($attribute->getAutoValue()) + $this->get('Default',$attribute, + $this->template->getContainer() ? $this->template->getContainer() : $this->getServer()->getContainerPath($this->template->getDN()), + 'autovalue'); + + # If this is the default template, we should mark all our attributes to show(). + if (($this->template->getID() == 'none') && (! $attribute->isInternal()) + && (($this->template->getContext() == 'edit' && $this->template->getID() == 'none') + || ($this->template->getContext() == 'create' && $attribute->getName() != 'objectclass'))) + $attribute->show(); + } + + if (DEBUGTMP||DEBUGTMPSUB) printf('* %s [Visit-End:%s]
',__METHOD__,get_class($this)); + + $this->visitEnd(); + } + } + + protected function getDefaultAttribute($attribute,$container,$type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + switch ($type) { + case 'autovalue': + $autovalue = $attribute->getAutoValue(); + + break; + + case 'helpervalue': + $autovalue = $attribute->getHelperValue(); + + break; + + default: + system_message(array( + 'title'=>_('Unknown Default Attribute context'), + 'body'=>sprintf('%s (%s)',_('A call was made to getDefaultAttribute() with an unkown context'),$type), + 'type'=>'warn')); + + return; + } + + $args = explode(';',$autovalue['args']); + $server = $this->getServer(); + $vals = ''; + + switch ($autovalue['function']) { + /** + * Function enables normal PHP functions to be called to evaluate a value. + * eg: =php.Function(date;dmY) + * + * All arguments will be passed to the function, and its value returned. + * If this used used in a POST context, the attribute values can be used as arguments. + * + * Mandatory Arguments: + * * arg 0 + * - php Function to call + * + * Additional arguments will be passed to the function. + */ + case 'Function': + $function = array_shift($args); + + if (count($args) && count($args) > 1) { + system_message(array( + 'title'=>_('Too many arguments'), + 'body'=>sprintf('%s (%s)',_('Function() only takes two arguments and more than two were specified'),count($args)), + 'type'=>'warn')); + + return; + } + + $function_args = explode(',',$args[0]); + + if (function_exists($function)) + $vals = call_user_func_array($function,$function_args); + + else + system_message(array( + 'title'=>_('Function doesnt exist'), + 'body'=>sprintf('%s (%s)',_('An attempt was made to call a function that doesnt exist'),$function), + 'type'=>'warn')); + + break; + + /** + * GetNextNumber will query the LDAP server and calculate the next number based on the query + * eg: + * + * Mandatory Arguments: + * * arg 0 + * - "$" => 'auto_number','search_base' in config file + * - "/",".",".." => get container parent as usual + * + * * arg 1 + * - attribute to query for + * + * Optional Arguments: + * * arg 2 (pool mechanism only) + * - "true" increments attribute by 1 + * - "false" do nothing + * + * * arg 3 (pool mechanism only) + * - ldap filter (must match one entry only in container) + * + * * arg 4 + * - calculus on number, eg: + * - *2,+1000 => number = (2*number) + 1000 + * + * * arg 5 + * - Min number + */ + case 'GetNextNumber': + # If the attribute already has values, we'll return + if ($type == 'autovalue' && $attribute->getValues()) + return; + + if ($args[0] == '$') + $args[0] = $server->getValue($this->server_id,'auto_number','search_base'); + + $container = $server->getContainerPath($container,$args[0]); + + $vals = get_next_number($container,$args[1], + (! empty($args[2]) && ($args[2] == 'false')) ? false : true, + (! empty($args[3])) ? $args[3] : false, + (! empty($args[5])) ? $args[5] : null); + + # Operate calculus on next number. + if (! empty($args[4])) { + $mod = explode(',',$args[4]); + $next_number = $vals; + + foreach ($mod as $calc) { + $operand = $calc{0}; + $operator = substr ($calc,1); + + switch ($operand) { + case '*': + $next_number = $next_number * $operator; + break; + + case '+': + $next_number = $next_number + $operator; + break; + + case '-': + $next_number = $next_number - $operator; + break; + + case '/': + $next_number = $next_number / $operator; + break; + } + } + + $vals = $next_number; + } + + break; + + /** + * PickList will query the LDAP server and provide a select list of values + * MultiList will query the LDAP server and provide a multi select list of values + * eg: + * + * eg: cn=root,nobody => cn=nobody;gidNumber;10)]]> + * + * Mandatory Arguments: + * * arg 0 + * - container, to query from current position + * - "/",".",".." => get container parent as usual + * + * * arg 1 + * - LDAP filter. May include '%attr%', it will be expanded. + * + * * arg2 + * - list attribute key + * + * Optional Arguments: + * * arg3 + * - select display (plus modifier /C: Capitalize) + * - replaced by %arg 2% if not given + * + * * arg 4 + * - the value furnished in output - must be attribute id. replaced by arg 2 if not given + * + * * arg 5 + * - container override + * + * * arg 6 + * - csv list (, separator) of added values. syntax: key => display_attribute=value, key... + * + * * arg 7 + * - csv list (, separator) of sort attributes (less to more important) + * + * * arg 8 (for MultiList) + * - size of displayed list (default: 10 lines) + */ + case 'MultiList': + case 'PickList': + # arg5 overrides our container + if (empty($args[5])) + $container = $server->getContainerPath($container,$args[0]); + else + $container = $args[5]; + + # Process filter (arg 1), eventually replace %attr% by it's value set in a previous page. + preg_match_all('/%(\w+)(\|.+)?(\/[lUC])?%/U',$args[1],$filtermatchall); + //print_r($matchall); // -1 = highlevel match, 1 = attr, 2 = subst, 3 = mod + + if (isset($_REQUEST['form'])) { + $formvalues = array_change_key_case($_REQUEST['form']); + + foreach ($filtermatchall[1] as $arg) { + $value = $formvalues[strtolower($arg)]; + $args[1] = preg_replace("/%($arg)(\|.+)?(\/[lU])?%/U",$value,$args[1]); + } + } + + if (empty($args[3])) + $args[3] = "%{$args[2]}%"; + + preg_match_all('/%(\w+)(\|.+)?(\/[lUC])?%/U',$args[3],$matchall); + //print_r($matchall); // -1 = highlevel match, 1 = attr, 2 = subst, 3 = mod + + $attrs = array_unique(array_merge($matchall[1],array($args[2]))); + + # arg7 is sort attributes + if (isset($args[7])) { + $sort_attrs = explode(',',$args[7]); + $attrs = array_unique(array_merge($attrs,$sort_attrs)); + } + + $picklistvalues = return_ldap_hash($container,$args[1],$args[2],$attrs,(isset($args[7]) && ($args[7])) ? $sort_attrs : false); + + # arg6 is a set of fixed values to add to search result + if (isset($args[6])) { + $fixedvalues = explode(',',$args[6]); + + foreach ($fixedvalues as $fixedvalue) { + if (empty($fixedvalue)) + continue; + + $fixedvalue = preg_split('/=\>/',$fixedvalue); + $displayvalue = explode('=',$fixedvalue[1]); + + $newvalue[trim($fixedvalue[0])] = array($args[2]=>trim($fixedvalue[0]),trim($displayvalue[0])=>trim($displayvalue[1])); + + $picklistvalues = array_merge($picklistvalues,$newvalue); + } + } + + $vals = array(); + + foreach ($picklistvalues as $key => $values) { + $display = $args[3]; + + foreach ($matchall[1] as $key => $arg) { + if (isset($values[$arg])) + $disp_val = $values[$arg]; + else + $disp_val = ''; + + if (is_array($disp_val)) + $disp_val = $disp_val[0]; + + if ($matchall[3][$key]) + switch ($matchall[3][$key]) { + case '/l': + # lowercase + if (function_exists('mb_convert_case')) + $disp_val = mb_convert_case($disp_val,MB_CASE_LOWER,'utf-8'); + else + $disp_val = strtolower($disp_val); + + break; + + case '/U': + # uppercase + if (function_exists('mb_convert_case')) + $disp_val = mb_convert_case($disp_val,MB_CASE_UPPER,'utf-8'); + else + $disp_val = strtoupper($disp_val); + + break; + + case '/C': + # capitalize + if (function_exists('mb_convert_case')) + $disp_val = mb_convert_case($disp_val,MB_CASE_TITLE,'utf-8'); + else + $disp_val = ucfirst($disp_val); + + break; + + default: + break; + } + + # make value a substring of + preg_match_all('/^\|([0-9]*)-([0-9]*)$/',trim($matchall[2][$key]),$substrarray); + + if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) { + $begin = $substrarray[1][0] ? $substrarray[1][0] : '0'; + $end = $substrarray[2][0] ? $substrarray[2][0] : strlen($disp_val); + + if (function_exists('mb_substr')) + $disp_val = mb_substr($disp_val,$begin,$end,'utf-8'); + else + $disp_val = substr($disp_val,$begin,$end); + } + + $display = preg_replace("/%($arg)(\|.+)?(\/[lUC])?%/U",$disp_val,$display); + } + + if (! isset($picklist[$values[$args[2]]])) { + $vals[$values[$args[2]]] = $display; + $picklist[$values[$args[2]]] = true; + } + } + + break; + + /** + * PasswordEncryptionTypes will return a list of our support password encryption types + * eg: =php.PasswordEncryptionTypes() + * + * This function doesnt use any arguments + */ + case 'PasswordEncryptionTypes': + $vals = password_types(); + + break; + + /** + * RandomPassword will create a random password for the value. + * eg: =php.RandomPassword() + * + * When calling the attribute Javascript it will generate a random password. + * + * This function doesnt use any arguments + */ + case 'RandomPassword': + break; + } + + switch ($type) { + case 'autovalue': + if (! is_array($vals)) + $attribute->autoValue(array($vals)); + else + $attribute->autoValue($vals); + + break; + + case 'helpervalue': + return $vals; + } + } + + /** + * Set the mode of the TemplateRender + * Applicable modes are "create" or "edit" + */ + protected function getMode() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->dn) + return 'modification'; + elseif ($this->container) + return 'creation'; + elseif (get_request('create_base')) + return 'creation'; + else + debug_dump_backtrace(sprintf('Unknown mode for %s',__METHOD__),1); + } + + /** + * Return the container for this mode + */ + protected function getModeContainer() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + switch ($this->getMode()) { + case 'creation': + return $this->container; + break; + + case 'modification': + return $this->dn; + break; + + default: + return null; + } + } + + /** + * Is the default template enabled? + */ + protected function haveDefaultTemplate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($_SESSION[APPCONFIG]->getValue('appearance','disable_default_template')) + return false; + else + return true; + } + + /** + * Present a list of available templates for creating and editing LDAP entries + */ + protected function drawTemplateChoice() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->drawTitle(); + $this->drawSubTitle(); + echo "\n"; + + switch ($this->getMode()) { + case 'creation': + $msg = _('Select a template for the creation process'); + break; + + case 'modification': + $msg = _('Select a template to edit the entry'); + break; + } + + $avail_templates = $this->getTemplates(); + $templates = $avail_templates->getTemplates($this->getMode(),$this->getModeContainer()); + printf('

%s

',$msg); + + $href_parms = array_to_query_string($_GET,array('meth')); + printf('
',htmlspecialchars($href_parms)); + echo "\n\n"; + + if (count($_POST)) { + echo '
'; + foreach ($_POST as $p => $v) + printf('',$p,$v); + echo '
'; + echo "\n\n"; + } + + echo ''; + echo ''; + printf('',_('Templates')); + echo ''; + + echo '
%s:'; + echo ''; + + $i = -1; + $nb_templates = count($templates); + + if ($this->haveDefaultTemplate()) + $nb_templates++; + + foreach ($templates as $name => $details) { + $i++; + + $isInValid = $details->isInValid(); + + # Balance the columns properly + if (($nb_templates % 2 == 0 && $i == intval($nb_templates / 2)) || + ($nb_templates % 2 == 1 && $i == intval($nb_templates / 2) + 1)) { + echo '
'; + } + + echo "\n"; + echo ''; + + if ($isInValid) + printf('',IMGDIR); + + else { + if (isAjaxEnabled()) + printf('', + htmlspecialchars($details->getID()),htmlspecialchars($details->getID()),htmlspecialchars($href_parms),$details->getID(),str_replace('\'','\\\'',_('Retrieving DN'))); + else + printf('', + htmlspecialchars($details->getID()),htmlspecialchars($details->getID())); + } + + printf('', + htmlspecialchars($details->getID()),$details->getIcon()); + printf(''; + echo ''; + } + echo "\n"; + + # Default template + if ($this->haveDefaultTemplate()) { + $i++; + + # Balance the columns properly + if (($nb_templates % 2 == 0 && $i == intval($nb_templates / 2)) || + ($nb_templates % 2 == 1 && $i == intval($nb_templates / 2) + 1)) { + echo '
Disabled
'; + } + + echo ''; + if (isAjaxEnabled()) + printf('', + htmlspecialchars($href_parms),'none',str_replace('\'','\\\'',_('Retrieving DN'))); + else + echo ''; + + printf('',IMGDIR); + printf('',_('Default')); + echo ''; + } + + echo '
'; + echo '
'; + echo '
'; + } + + /** VISIT METHODS **/ + + /** + * This function will setup our template object (read LDAP for current values, read $_REQUEST for new values, etc) + * so that it can be rendered. + */ + private function visitStart() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + # If we have a DN, then we are an editing template + if ($this->dn) + $this->template->setDN($this->dn); + + # Else if we have a container, we are a creating template + elseif ($this->container || get_request('create_base')) + $this->template->setContainer($this->container); + + else + debug_dump_backtrace('Dont know what type of template we are - no DN or CONTAINER?',1); + + # Header + $this->drawHeader(); + } + + private function visitEnd() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + foreach ($this->template->getAttributesShown() as $attribute) + if ($attribute->getPage() > $this->pagelast) + $this->pagelast = $attribute->getPage(); + echo "\n\n"; + + if ($this->template->getContext() == 'create') { + $this->drawStepTitle($this->page); + $this->drawStepFormStart($this->page); + $this->drawStepForm($this->page); + $this->drawStepFormEnd(); + + } elseif ($this->template->getContext() == 'copyasnew') { + $this->drawStepFormStart($this->page); + printf('',$this->template->getContainer(false)); + echo '
'; + $this->drawRDNChooser(); + echo '
'; + $this->drawForm(true); + $this->drawStepFormSubmitButton($this->page); + + } else { + # Draw internal attributes + if (get_request('show_internal_attrs','REQUEST')) { + echo ''; + $this->drawInternalAttributes(); + echo '

'; + echo "\n"; + } + + $this->drawFormStart(); + + # To support our AJAX add Attribute + printf('
','none'); + + $this->drawForm(); + $this->drawStepFormEnd(); + } + } + + /** PAGE DRAWING METHODS **/ + + private function drawHeader() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + # Title + $this->drawTitle(); + if (get_request('create_base')) + $this->drawSubTitle(sprintf('%s: %s',_('Creating Base DN'),$this->template->getDN())); + else + $this->drawSubTitle(); + echo "\n"; + + # Menu + $this->drawMenu(); + } + + public function drawTitle($title=null) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (is_null($title)) + switch ($this->getMode()) { + case 'creation': + $title = _('Create Object'); + break; + + case 'modification': + $title = htmlspecialchars(get_rdn($this->dn)); + break; + + default: + $title = 'Title'; + } + + parent::drawTitle($title); + } + + public function drawSubTitle($subtitle=null) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if ($subtitle) + return parent::drawSubTitle($subtitle); + + switch ($this->getMode()) { + case 'creation': + $subtitle = sprintf('%s: %s   %s: %s', + _('Server'),$this->getServer()->getName(), + _('Container'),htmlspecialchars($this->container)); + + if ($this->template_id) { + $subtitle .= '
'; + $subtitle .= sprintf('%s: %s',_('Template'),$this->template->getID() != 'none' ? $this->template->getTitle() : _('Default')); + if ($this->template->getName()) + $subtitle .= sprintf(' (%s)',$this->template->getName(false)); + } + + break; + + case 'modification': + $subtitle = sprintf('%s: %s', + _('DN'),htmlspecialchars($this->dn)); + + break; + } + + parent::drawSubTitle($subtitle); + } + + /** PAGE ENTRY MENU **/ + + private function drawMenu() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + # We only have a menu for editing entries. + if ($this->template->getContext() == 'edit') { + + echo ''; + echo ''; + $menuitem_number = 0; + + foreach (array('entryrefresh','showinternal','switchtemplate','entryexport','entrycopy','entrydelete','entryrename','entrycompare','childcreate','addattr','msgdel','childview','childexport','msgschema','msgro','msgmodattr') as $item) { + $item = $this->getMenuItem($item); + + if ($item) { + $endofrow = false; + $start = true; + $it = ''; // menu item + $ms = ''; // item message + + if (is_array($item) && count($item) > 0) { + $it = $item[0]; + + if (count($item) > 1) + $ms = $item[1]; + + } else { + $it = $item; + } + + if ($it) { + $menuitem_number++; + echo $it; + + if ($ms) { + if (($menuitem_number % 2) == 1) { + $menuitem_number++; + echo ''; + $endofrow = false; + $start = false; + } + + if ($endofrow) + print $ms; + else + echo "$ms"; + + echo ''; + $endofrow = true; + $start = false; + + } else { + if ($menuitem_number > 1 && ($menuitem_number % 2) == 0) { + echo ''; + $endofrow = true; + $start = false; + } + } + + } elseif ($ms) { + if (($menuitem_number % 2) == 1) { + $menuitem_number++; + echo ''; + $endofrow = false; + $start = false; + } + + if ($endofrow || $start) + print $ms; + else + echo "$ms"; + + echo ''; + $endofrow = true; + $start = false; + } + + echo "\n"; + } + } + + if (($menuitem_number % 2) == 1) + echo ''; + else + echo ''; + + echo ''; + echo ''; + } + } + + /** PAGE ENTRY MENU ITEMS **/ + + private function getMenuItem($i) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s (%s)
',__METHOD__,$i); + + switch ($i) { + case 'entryrefresh': + if ($_SESSION[APPCONFIG]->isCommandAvailable('cmd','entry_refresh')) + return $this->getMenuItemRefresh(); + else + return ''; + + case 'switchtemplate': + if ($_SESSION[APPCONFIG]->isCommandAvailable('cmd','switch_template')) + return $this->getMenuItemSwitchTemplate(); + else + return ''; + + case 'entryexport': + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','export_form') && $_SESSION[APPCONFIG]->isCommandAvailable('script','export')) + return $this->getMenuItemExportBase(); + else + return ''; + + case 'entrycopy': + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','copy_form') && $_SESSION[APPCONFIG]->isCommandAvailable('script','copy') && ! $this->template->isReadOnly()) + return $this->getMenuItemMove(); + else + return ''; + + case 'showinternal': + if ($_SESSION[APPCONFIG]->isCommandAvailable('cmd','entry_internal_attributes_show')) + return $this->getMenuItemInternalAttributes(); + else + return ''; + + case 'entrydelete': + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','delete_form') && $_SESSION[APPCONFIG]->isCommandAvailable('script','delete') && ! $this->template->isReadOnly()) + return $this->getMenuItemDelete(); + else + return ''; + + case 'entryrename': + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','rename_form') && $_SESSION[APPCONFIG]->isCommandAvailable('script','rename') && ! $this->template->isReadOnly()) { + + # Check if any of the RDN's are read only. + $rdnro = false; + foreach ($this->template->getRDNAttributeName() as $attr) { + $attribute = $this->template->getAttribute($attr); + + if ($attribute && $attribute->isVisible() && ! $attribute->isReadOnly()) { + $rdnro = true; + break; + } + } + + if (! $rdnro) + return $this->getMenuItemRename(); + } + + return ''; + + case 'msgdel': + if ($_SESSION[APPCONFIG]->getValue('appearance','show_hints') + && $_SESSION[APPCONFIG]->isCommandAvailable('script','delete_form') && $_SESSION[APPCONFIG]->isCommandAvailable('script','delete') && ! $this->template->isReadOnly()) + return array('',$this->getDeleteAttributeMessage()); + else + return ''; + + case 'entrycompare': + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','compare_form') && $_SESSION[APPCONFIG]->isCommandAvailable('script','compare') && ! $this->template->isReadOnly()) + return $this->getMenuItemCompare(); + else + return ''; + + case 'childcreate': + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','create') && ! $this->template->isReadOnly() && ! $this->template->isNoLeaf()) + return $this->getMenuItemCreate(); + else + return ''; + + case 'addattr': + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','add_attr_form') && ! $this->template->isReadOnly()) + return $this->getMenuItemAddAttribute(); + else + return ''; + + case 'childview': + case 'childexport': + static $children_count = false; + static $more_children = false; + + $tree = get_cached_item($this->getServerID(),'tree'); + $tree_item = $tree->getEntry($this->template->getDN()); + + if (! $tree_item) { + $tree->addEntry($this->template->getDN()); + $tree_item = $tree->getEntry($this->template->getDN()); + } + + if ($children_count === false) { + # Visible children in the tree + $children_count = count($tree_item->getChildren()); + # Is there filtered children ? + $more_children = $tree_item->isSizeLimited(); + + if (! $children_count || ! $more_children) { + # All children in ldap + $all_children = $this->getServer()->getContainerContents( + $this->template->getDN(),null,$children_count+1,'(objectClass=*)',$_SESSION[APPCONFIG]->getValue('deref','view'),null); + + $more_children = (count($all_children) > $children_count); + } + } + + if ($children_count > 0 || $more_children) { + if ($children_count <= 0) + $children_count = ''; + if ($more_children) + $children_count .= '+'; + + if ($i == 'childview') + return $this->getMenuItemShowChildren($children_count); + elseif ($i == 'childexport' && $_SESSION[APPCONFIG]->isCommandAvailable('script','export_form') && $_SESSION[APPCONFIG]->isCommandAvailable('script','export')) + return $this->getMenuItemExportSub(); + else + return ''; + + } else + return ''; + + case 'msgschema': + if ($_SESSION[APPCONFIG]->getValue('appearance','show_hints') && $_SESSION[APPCONFIG]->isCommandAvailable('script','schema')) + return array('',$this->getViewSchemaMessage()); + else + return array(); + + case 'msgro': + if ($this->template->isReadOnly()) + return array('',$this->getReadOnlyMessage()); + else + return array(); + + case 'msgmodattr': + $modified_attrs = array(); + $modified = get_request('modified_attrs','REQUEST',false,array()); + + foreach ($this->template->getAttributes(true) as $attribute) + if (in_array($attribute->getName(),$modified)) + array_push($modified_attrs,$attribute->getFriendlyName()); + + if (count($modified_attrs)) + return array('',$this->getModifiedAttributesMessage($modified_attrs)); + else + return array(); + + default: + return false; + } + } + + protected function getDeleteAttributeMessage() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + if ($_SESSION[APPCONFIG]->isCommandAvailable('script','delete_attr') && ! $this->template->isReadOnly()) + return sprintf($this->layout['hint'],_('Hint: To delete an attribute, empty the text field and click save.')); + else + return ''; + } + + protected function getModifiedAttributesMessage(&$modified_attributes) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + return sprintf($this->layout['hint'], + (count($modified_attributes) == 1) ? + sprintf(_('An attribute (%s) was modified and is highlighted below.'),implode('',$modified_attributes)) : + sprintf(_('Some attributes (%s) were modified and are highlighted below.'),implode(', ',$modified_attributes))); + } + + protected function getReadOnlyMessage() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + return sprintf($this->layout['hint'],_('Viewing entry in read-only mode.')); + } + + protected function getViewSchemaMessage() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + return sprintf($this->layout['hint'],_('Hint: To view the schema for an attribute, click the attribute name.')); + } + + /** PAGE ENTRY MENU ITEMS DETAILS **/ + + private function getMenuItemRefresh() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=template_engine&%s&junk=%s',$this->url_base,random_junk()); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'refresh.png',_('Refresh'), + htmlspecialchars($href),_('Refresh this entry'),htmlspecialchars($href),str_replace('\'','\\\'',_('Reloading')),_('Refresh')); + else + return sprintf($this->layout['action'],IMGDIR,'refresh.png',_('Refresh'), + htmlspecialchars($href),_('Refresh this entry'),_('Refresh')); + } + + protected function getMenuItemSwitchTemplate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $href = sprintf('cmd=template_engine&%s&template=',$this->url_base); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'switch.png',_('Switch Template'), + htmlspecialchars($href),_('Change to another template'),htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Switch Template')); + else + return sprintf($this->layout['action'],IMGDIR,'switch.png',_('Switch Template'), + htmlspecialchars($href),_('Change to another template'),_('Switch Template')); + } + + protected function getMenuItemExportBase() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=export_form&%s&scope=base',$this->url_base); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'export.png',_('Export'), + htmlspecialchars($href),_('Save a dump of this object'),htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Export')); + else + return sprintf($this->layout['action'],IMGDIR,'export.png',_('Export'), + htmlspecialchars($href),_('Save a dump of this object'),_('Export')); + } + + private function getMenuItemMove() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=copy_form&%s',$this->url_base); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'cut.png',_('Cut'), + htmlspecialchars($href),_('Copy this object to another location, a new DN, or another server'), + htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Copy or move this entry')); + else + return sprintf($this->layout['action'],IMGDIR,'cut.png',_('Cut'), + htmlspecialchars($href),_('Copy this object to another location, a new DN, or another server'), + _('Copy or move this entry')); + } + + protected function getMenuItemInternalAttributes() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (get_request('show_internal_attrs','REQUEST')) { + $href = sprintf('cmd=template_engine&%s&junk=',$this->url_base,random_junk()); + + return sprintf($this->layout['action'],IMGDIR,'tools-no.png',_('Hide'), + htmlspecialchars($href),'',_('Hide internal attributes')); + + } else { + $href = sprintf('cmd=template_engine&show_internal_attrs=true&%s',$this->url_base); + + return sprintf($this->layout['action'],IMGDIR,'tools.png',_('Show'), + htmlspecialchars($href),'',_('Show internal attributes')); + } + } + + private function getMenuItemDelete() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=delete_form&%s',$this->url_base); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'trash.png',_('Trash'), + htmlspecialchars($href),_('You will be prompted to confirm this decision'), + htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Delete this entry')); + else + return sprintf($this->layout['action'],IMGDIR,'trash.png',_('Trash'), + htmlspecialchars($href),_('You will be prompted to confirm this decision'),_('Delete this entry')); + } + + protected function getMenuItemRename() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=rename_form&%s&template=%s',$this->url_base,$this->template->getID()); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'rename.png',_('Rename'), + htmlspecialchars($href),_('Rename this entry'),htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Rename')); + else + return sprintf($this->layout['action'],IMGDIR,'rename.png',_('Rename'), + htmlspecialchars($href),_('Rename this entry'),_('Rename')); + } + + protected function getMenuItemCompare() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=compare_form&%s',$this->url_base); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'compare.png',_('Compare'), + htmlspecialchars($href),_('Compare this entry with another'), + htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Compare with another entry')); + else + return sprintf($this->layout['action'],IMGDIR,'compare.png',_('Compare'), + htmlspecialchars($href),_('Compare this entry with another'),_('Compare with another entry')); + } + + protected function getMenuItemCreate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=template_engine&server_id=%s&container=%s',$this->getServerID(),$this->template->getDNEncode()); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'create.png',_('Create'), + htmlspecialchars($href),_('Create a child entry'), + htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Create a child entry')); + else + return sprintf($this->layout['action'],IMGDIR,'create.png',_('Create'), + htmlspecialchars($href),_('Create a child entry'),_('Create a child entry')); + } + + protected function getMenuItemAddAttribute() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (! $this->template->getAvailAttrs()) + return ''; + + $href = sprintf('cmd=add_attr_form&%s',$this->url_base); + $layout = '%s%s'; + + if (isAjaxEnabled()) + return sprintf($layout,IMGDIR,'add.png',_('Add'), + htmlspecialchars($href),_('Add new attribute to this object'), + htmlspecialchars($href),str_replace('\'','\\\'',_('Add new attribute')),_('Add new attribute')); + else + return sprintf($this->layout['action'],IMGDIR,'add.png',_('Add'), + htmlspecialchars($href),_('Add new attribute to this object'),_('Add new attribute')); + } + + protected function getMenuItemShowChildren($children_count) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=query_engine&server_id=%s&filter=%s&base=%s&scope=one&query=none&size_limit=0&search=true', + $this->getServerID(),rawurlencode('objectClass=*'),$this->template->getDNEncode()); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'children.png',_('Children'), + htmlspecialchars($href),_('View the children of this object'), + htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')), + ($children_count == 1) ? _('View 1 child') : sprintf(_('View %s children'),$children_count)); + else + return sprintf($this->layout['action'],IMGDIR,'children.png',_('Children'), + htmlspecialchars($href),_('View the children of this object'), + ($children_count == 1) ? _('View 1 child') : sprintf(_('View %s children'),$children_count)); + } + + protected function getMenuItemExportSub() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=export_form&%s&scope=%s',$this->url_base,'sub'); + + if (isAjaxEnabled()) + return sprintf($this->layout['actionajax'],IMGDIR,'export.png',_('Save'), + htmlspecialchars($href),_('Save a dump of this object and all of its children'), + htmlspecialchars($href),str_replace('\'','\\\'',_('Loading')),_('Export subtree')); + else + return sprintf($this->layout['action'],IMGDIR,'export.png',_('Save'), + htmlspecialchars($href),_('Save a dump of this object and all of its children'),_('Export subtree')); + } + + /** CHOOSERS **/ + + /** + * RDN Chooser + */ + protected function drawRDNChooser() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (! count($this->template->getRDNAttrs())) { + printf('%s','RDN'); + + echo ''; + + } else { + echo ''; + foreach ($this->template->getRDNAttrs() as $rdn) + printf('',htmlspecialchars($rdn)); + + if (get_request('create_base')) + echo ''; + + echo ''; + } + } + + /** + * Container Chooser + */ + protected function drawContainerChooser($default_container) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + echo ''; + printf('%s',_('Container')); + echo ''; + if (get_request('create_base')) + printf('%s',$default_container,htmlspecialchars($default_container)); + else { + printf('',htmlspecialchars($default_container)); + draw_chooser_link('entry_form','container'); + } + echo ''; + echo ''; + } + + /** + * Object Class Chooser + */ + protected function drawObjectClassChooser() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $socs = $this->getServer()->SchemaObjectClasses(); + if (! $socs) + $socs = array(); + + echo ''; + printf('%s',_('ObjectClasses')); + echo ''; + echo ''; + echo ''; + + if ($_SESSION[APPCONFIG]->getValue('appearance','show_hints')) { + printf(' Hint',IMGDIR); + echo _('Hint: You must choose exactly one structural objectClass (shown in bold above)'); + echo '
'; + } + } + + /** INTERNAL ATTRIBUTES **/ + + protected function drawInternalAttributes() { + if ($this->template->getAttributesInternal()) + foreach ($this->template->getAttributesInternal() as $attribute) + $this->draw('Internal',$attribute); + else + printf('(%s)
',_('No internal attributes')); + + echo "\n"; + } + + protected function drawInternalAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->draw('Template',$attribute); + } + + /** FORM METHODS **/ + + public function drawFormStart() { + echo '
'; + + echo '
'; + if ($_SESSION[APPCONFIG]->getValue('confirm','update')) + echo ''; + else + echo ''; + echo '
'; + } + + protected function drawForm($nosubmit=false) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + echo '
'; + printf('',$this->getServerID()); + printf('',$this->template->getDNEncode(false)); + printf('',$this->template->getID()); + echo '
'; + + echo ''; + + $this->drawShownAttributes(); + if (! $nosubmit) + $this->drawFormSubmitButton(); + + echo '
'; + + echo '
 '; + $this->drawHiddenAttributes(); + echo '
'; + } + + public function drawFormEnd() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + # Include the RDN details to support creating the base + if (get_request('create_base')) { + if (get_request('rdn')) { + $rdn = explode('=',get_request('rdn')); + echo '
'; + printf('',$rdn[0],$rdn[1]); + printf('',$rdn[0]); + echo '
'; + } + } + + echo '
'; + + # Javascript + $this->drawJavascript(); + + # For debugging, show the template object. + if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_debug_info') && get_request('debug','GET')) { + echo "\n\n"; + printf('Plus',IMGDIR); + echo ''; + } + } + + public function drawFormSubmitButton() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (! $this->template->isReadOnly()) + // @todo cant use AJAX here, it affects file uploads. + printf('', + _('Update Object')); + } + + /** STEP FORM METHODS **/ + + private function drawStepTitle($page) { + if (DEBUGTMP) printf('%s
',__METHOD__); + if (DEBUGTMP||DEBUGTMPSUB) printf('* %s [templateNAME:%s]
',__METHOD__,$this->template->getName()); + + # The default template needs to ask the user for objectClasses. + if ($this->template->isType('default')) { + # The default template only uses 2 pages + $this->pagelast = 2; + + echo '

'; + printf('%s: ',sprintf(_('Step %s of %s'),$page,$this->pagelast)); + + if ($page == 1) + echo _('Container and ObjectClass(es)'); + else + echo _('Specify attributes and values'); + + echo '

'; + + } elseif ($this->template->getDescription()) + printf('

%s (%s)

', + _($this->template->getDescription()), + sprintf(_('Step %s of %s'),$page,$this->pagelast)); + } + + private function drawStepFormStart($page) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (($this->template->isType('default') && $this->template->getContext() == 'create' && $page == 1) || $page < $this->pagelast) { + echo '
'; + echo '
'; + + } else { + echo ''; + echo '
'; + + if ($_SESSION[APPCONFIG]->getValue('confirm','create') && ! get_request('create_base')) + echo ''; + else + echo ''; + } + } + + protected function drawStepForm($page) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + printf('',$this->getServerID()); + printf('',$this->template->getID()); + printf('',$page+1); + if (get_request('create_base')) + echo ''; + + $this->drawHiddenAttributes(); + + if ($this->template->isType('default') && $page == 1) { + echo '
'; + + echo ''; + + $this->drawContainerChooser($this->template->getContainer()); + $this->drawObjectClassChooser(); + + } else { + printf('',$this->template->getContainerEncode(false)); + echo ''; + + echo '
'; + + $this->drawRDNChooser(); + + if ($this->template->isType('default') && $this->template->getContext() == 'create') + $this->drawStepFormDefaultAttributes(); + else + $this->drawShownAttributes(); + } + + $this->drawStepFormSubmitButton($page); + + echo '
'; + } + + private function drawStepFormEnd() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->drawFormEnd(); + } + + private function drawStepFormSubmitButton($page) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + echo ''; + if ($page < $this->pagelast) + printf(' ',_('Proceed >>')); + else + // @todo cant use AJAX here, it affects file uploads. + printf('', + _('Create Object')); + echo ''; + } + + /** + * Given our known objectClass in the template, this will render the required MAY and optional MUST attributes + */ + private function drawStepFormDefaultAttributes() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + # Put required attributes first + $attrs = array(); + $attrs['required'] = array(); + $attrs['optional'] = array(); + foreach ($this->template->getAttributes(true) as $attribute) { + # Skip the objectclass attribute, we already know it in a default creation form. + if ($attribute->getName() == 'objectclass') + continue; + + if ($attribute->isRequired()) + array_push($attrs['required'],$attribute); + + elseif (! $attribute->getValues()) + array_push($attrs['optional'],$attribute); + } + + printf('%s',_('Required Attributes')); + if (count($attrs['required'])) + foreach ($attrs['required'] as $attribute) + $this->draw('Template',$attribute); + + else + printf('(%s)',_('none')); + + printf('%s',_('Optional Attributes')); + if (count($attrs['optional'])) + foreach ($attrs['optional'] as $attribute) + $this->draw('Template',$attribute); + + else + printf('(%s)',_('none')); + + echo "\n"; + } + + /** DRAW ATTRIBUTES **/ + + private function drawShownAttributes() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + foreach ($this->template->getAttributesShown() as $attribute) + if (($attribute->getPage() == $this->page) && ($attribute->isRequired() || $attribute->isMay())) { + $this->draw('Template',$attribute); + echo "\n"; + } + } + + /** DRAW PAGE JAVACRIPT */ + + protected function drawJavascript() { + if (DEBUGTMP) printf('%s
',__METHOD__); + + echo "\n"; + printf('',__METHOD__); + echo "\n"; + + printf('',JSDIR); + printf('',JSDIR); + printf('',JSDIR); + echo "\n"; + + printf('',__METHOD__); + echo ' +'; + echo "\n"; + printf('',__METHOD__); + echo "\n"; + + $this->drawTemplateJavascript(); + + # For DateAttributes, we need to set some defaults for the js_calendar. + echo ''."\n"; + echo ''."\n"; + echo ''."\n"; + echo "\n"; + + foreach ($this->template->getAttributesShown() as $attribute) + $this->draw('Javascript',$attribute); + + // @todo We need to sleep here a little bit, because our JS may not have loaded yet. + echo ''."\n"; + printf('',__METHOD__); + echo "\n"; + } + + /** + * Javascript Functions + */ + private function drawTemplateJavascript() { + printf('',__METHOD__); + echo "\n"; + foreach ($this->template->getAttributes(true) as $attribute) + if ($onchange = $attribute->getOnChange()) + if (is_array($onchange)) + foreach ($onchange as $value) + $this->template->OnChangeAdd($attribute->getName(),$value); + else + $this->template->OnChangeAdd($attribute->getName(),$onchange); + printf('',__METHOD__); + echo "\n"; + + printf('',__METHOD__); + + echo ' +'; + echo "\n"; + printf('',__METHOD__); + echo "\n"; + } + + /** ATTRIBUTE TITLE **/ + + protected function drawTitleAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (($this->template->getContext() == 'edit') + && ($attribute->hasBeenModified() || in_array($attribute->getName(),get_request('modified_attrs','REQUEST',false,array())))) + echo ''; + else + echo ''; + + echo ''; + $this->draw('Name',$attribute); + echo ''; + + echo ''; + + # Setup the $attr_note, which will be displayed to the right of the attr name (if any) + if ($_SESSION[APPCONFIG]->getValue('appearance','show_attribute_notes')) + $this->draw('Notes',$attribute); + + echo ''; + echo ''; + } + + /** ATTRIBUTE LINE **/ + + protected function drawStartValueLineAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (($this->template->getContext() == 'edit') + && ($attribute->hasBeenModified() || in_array($attribute->getName(),get_request('modified_attrs','REQUEST',false,array())))) + echo ''; + else + echo ''; + + echo ''; + } + + protected function drawEndValueLineAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + echo ''; + echo ''; + + if ($attribute->getSpacer()) + echo ' '; + + if (($this->template->getContext() == 'edit') + && ($attribute->hasBeenModified() || in_array($attribute->getName(),get_request('modified_attrs','REQUEST',false,array())))) + echo ''; + } + + protected function drawTemplateAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->draw('Title',$attribute); + $this->draw('TemplateValues',$attribute); + } + + protected function drawTemplateValuesAttribute($attribute) { + if (DEBUGTMP) printf('%s:%s
',time(),__METHOD__); + + $this->draw('StartValueLine',$attribute); + + # Draws values + $value_count = $attribute->getValueCount(); + for ($i=0;$i<$value_count;$i++) + $this->draw('Value',$attribute,$i); + + if (! $attribute->isInternal()) { + $blankvalue_count = $attribute->getMaxValueCount(); + if ($blankvalue_count < 0) + $blankvalue_count = 1; + + $blankvalue_count -= $value_count; + + for ($j=0;$j<$blankvalue_count;$j++) + $this->draw('Value',$attribute,$i+$j); + + if (($value_count == $blankvalue_count) || ($value_count && $blankvalue_count < 1)) + $this->draw('Menu',$attribute); + } + + $this->draw('EndValueLine',$attribute); + echo "\n"; + } + + /** DRAW ICONS FOR ATTRIBUTES VALUES **/ + + protected function drawIconAttribute($attribute,$val) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (is_dn_string($val) || $this->getServer()->isDNAttr($attribute->getName())) + $this->draw('DnValueIcon',$attribute,$val); + elseif (is_mail_string($val)) + $this->draw('MailValueIcon',$attribute,$val); + elseif (is_url_string($val)) + $this->draw('UrlValueIcon',$attribute,$val); + + else { + if ($icon = $attribute->getIcon()) + printf('Icon ',$icon); + } + } + + protected function drawDnValueIconAttribute($attribute,$val) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (strlen($val) <= 0) + printf('Go ',IMGDIR); + elseif ($this->getServer()->dnExists($val)) + printf('Go ', + $this->getServerID(),rawurlencode($val),_('Go to'),$val,IMGDIR); + else + printf('Go ',_('DN not available'),$val,IMGDIR); + } + + protected function drawMailValueIconAttribute($attribute,$val) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $img = sprintf('%s',IMGDIR,_('Mail')); + if (strlen($val) <= 0) + echo $img; + else + printf('%s',htmlspecialchars($val),$img); + echo ' '; + } + + protected function drawUrlValueIconAttribute($attribute,$val) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $img = sprintf('%s',IMGDIR,_('URL')); + $url = explode(' +',$val,2); + + if (strlen($val) <= 0) + echo $img; + else + printf('%s',htmlspecialchars($url[0]),$img); + echo ' '; + } + + /** DEFAULT ATTRIBUTE RENDERING **/ + + /** javacript */ + + protected function drawJavascriptAttribute($attribute) { + if (! $attribute->needJS()) { + printf('',$attribute->getName()); + echo "\n"; + return; + } + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + + echo ''."\n"; + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + } + + protected function getFocusJavascriptAttribute($attribute,$component) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + return ''; + } + + protected function getBlurJavascriptAttribute($attribute,$component) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $j = "\t".'fill('.$component.'.id,pla_getComponentValue('.$component.'));'."\n"; + $j .= "\t".'validate_'.$attribute->getName().'('.$component.',false);'."\n"; + + return $j; + } + + protected function drawFocusJavascriptAttribute($attribute,$component) { + echo $this->get('FocusJavascript',$attribute,$component); + } + + protected function drawBlurJavascriptAttribute($attribute,$component) { + echo $this->get('BlurJavascript',$attribute,$component); + } + + protected function drawFillJavascriptAttribute($attribute,$component_id,$component_value) { + if ($attribute->needJS('validate')) + printf("\tvalidate_%s(pla_getComponentById(%s),true);\n",$attribute->getName(),$component_id); + } + + protected function drawValidateJavascriptAttribute($attribute,$component,$silence,$var_valid) { + printf('var vals = getAttributeValues("new","%s");',$attribute->getName()); + echo 'if (vals.length <= 0) {'; + printf('%s = false;',$var_valid); + printf('alertError("%s: %s",%s);',_('This attribute is required'),$attribute->getFriendlyName(),$silence); + echo '}'; + echo "\n"; + + printf('var comp = getAttributeComponents("new","%s");',$attribute->getName()); + echo 'for (var i = 0; i < comp.length; i++) {'; + printf('comp[i].style.backgroundColor = "%s";',$var_valid ? 'white' : '#FFFFA0'); + echo '}'; + } + + /** ATTRIBUTE MENU **/ + + protected function drawMenuAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $result = ''; + $item = ''; + + foreach (array('add','modify','rename') as $action) + if ($item = $this->get('MenuItem',$attribute,$action)) + $result .= sprintf('
%s
',$item); + + if (! $result) + return; + + echo ''; + printf('',$result); + echo ''; + echo '
 %s
'; + } + + protected function getMenuItemAttribute($attribute,$action) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + # If there is no DN, then this is a creating entry. + if (($this->template->getContext() == 'create') || $this->template->isReadOnly()) + return false; + + switch ($action) { + case 'add': + if ($attribute->isVisible() && ! $attribute->isReadOnly() + && $_SESSION[APPCONFIG]->isCommandAvailable('script','add_value_form')) { + + if ($attribute->haveMoreValues()) + return $this->get('AddValueMenuItem',$attribute); + } + + return ''; + + case 'modify': + if (in_array($attribute->getName(),arrayLower($_SESSION[APPCONFIG]->getValue('modify_member','groupattr')))) { + if ($attribute->isVisible() && ! $attribute->isReadOnly() && ! $attribute->isRDN() + && $_SESSION[APPCONFIG]->isCommandAvailable('script','modify_member_form')) + return $this->get('ModifyMemberMenuItem',$attribute); + } + + return ''; + + case 'rename': + if ($attribute->isVisible() && $attribute->isRDN() && ! $attribute->isReadOnly() + && $_SESSION[APPCONFIG]->isCommandAvailable('script','rename_form') + && $_SESSION[APPCONFIG]->isCommandAvailable('script','rename')) + return $this->get('RenameMenuItem',$attribute); + + return ''; + + default: + return false; + } + } + + protected function getAddValueMenuItemAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href_parm = htmlspecialchars(sprintf('cmd=add_value_form&server_id=%s&dn=%s&attr=%s', + $this->getServerID(),$this->template->getDNEncode(),rawurlencode($attribute->getName(false)))); + + if (isAjaxEnabled()) + return sprintf('(%s)', + $href_parm,_('Add an additional value to attribute'),$attribute->getName(false),$attribute->getName(), + $href_parm,str_replace('\'','\\\'',_('Add Value to Attribute')),_('add value')); + else + return sprintf('(%s)', + $href_parm,_('Add an additional value to attribute'),$attribute->getName(false),_('add value')); + } + + protected function getAddValueMenuItemObjectClassAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href_parm = htmlspecialchars(sprintf('cmd=add_value_form&server_id=%s&dn=%s&attr=%s', + $this->getServerID(),$this->template->getDNEncode(),rawurlencode($attribute->getName(false)))); + + if (isAjaxEnabled()) + return sprintf('(%s)', + $href_parm,_('Add an additional value to attribute'),$attribute->getName(false), + $href_parm,str_replace('\'','\\\'',_('Add Value to Attribute')),_('add value')); + else + return sprintf('(%s)', + $href_parm,_('Add an additional value to attribute'),$attribute->getName(false),_('add value')); + } + + protected function getModifyMemberMenuItemAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd=modify_member_form&server_id=%s&dn=%s&attr=%s', + $this->getServerID(),$this->template->getDNEncode(),rawurlencode($attribute->getName())); + + if (isAjaxEnabled()) + return sprintf('(%s)', + htmlspecialchars($href),_('Modify members for'),$this->template->getDN(), + htmlspecialchars($href),str_replace('\'','\\\'',_('Modify group membership')), + _('modify group members')); + else + return sprintf('(%s)', + htmlspecialchars($href),_('Modify members for'),$this->template->getDN(),_('modify group members')); + } + + protected function getRenameMenuItemAttribute($attribute) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + $href = sprintf('cmd.php?cmd=rename_form&server_id=%s&dn=%s&template=%s', + $this->getServerID(),$this->template->getDNEncode(),$this->template->getID()); + + return sprintf('(%s)',htmlspecialchars($href),_('rename')); + } + + /** values **/ + + protected function drawValueAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if ($attribute->isMultiple() && $i > 0) + return; + + $val = $attribute->getValue($i); + + if ($attribute->isVisible()) { + echo ''; + + echo ''; + + echo '
'; + $this->draw('Icon',$attribute,$val); + echo ''; + } + + if ($attribute->isInternal()) + $this->draw('FormReadOnlyValue',$attribute,$i); + else + $this->draw('FormValue',$attribute,$i); + + if ($attribute->isVisible()) { + echo ''; + $this->draw('RequiredSymbol',$attribute); + echo '
'; + } + echo "\n"; + } + + # @todo for userPasswords, we need to capture the default value of select lists, without specifying + protected function drawHelperAttribute($attribute,$i) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $params = $attribute->getHelper(); + + # We take the first only + $id = isset($params['id']) ? $params['id'] : ''; + if (is_array($id)) { + system_message(array( + 'title'=>_('Too many arguments'), + 'body'=>sprintf('%s (%s)',_('Helper attribute has too many ID values, only the first one is used'),count($id)), + 'type'=>'warn')); + + $id = $id[0]; + } + + # We take the first only + $display = isset($params['display']) ? $params['display'] : ''; + if (is_array($display)) { + system_message(array( + 'title'=>_('Too many arguments'), + 'body'=>sprintf('%s (%s)',_('Helper attribute has too many DISPLAY values, only the first one is used'),count($display)), + 'type'=>'warn')); + + $display = $display[0]; + } + + # We take the first only + $default = isset($params['default']) ? $params['default'] : ''; + if (is_array($default)) { + system_message(array( + 'title'=>_('Too many arguments'), + 'body'=>sprintf('%s (%s)',_('Helper attribute has too many DISPLAY values, only the first one is used'),count($default)), + 'type'=>'warn')); + + $default = $default[0]; + } + + if ($attribute->getHelperValue()) + $vals = $this->get('Default',$attribute, + $this->template->getContainer() ? $this->template->getContainer() : $this->getServer()->getContainerPath($this->template->getDN()), + 'helpervalue'); + else + $vals = isset($params['value']) ? $params['value'] : ''; + + if ($this->template->getContext() == 'create') + $dn = $this->template->getContainer(); + else + $dn = $this->template->getDN(); + + if (is_array($vals) && count($vals) > 0) { + $found = false; + + printf(''; + + } else { + # Vals must be an empty array. + if (is_array($vals)) + $vals = ''; + + printf('', + $id,htmlspecialchars($attribute->getName()),$i, + $id,htmlspecialchars($attribute->getName()),$i, + htmlspecialchars($vals)); + } + + if ($display) { + echo '
'; + printf('%s',$display); + echo '
'; + } + } + + protected function drawRequiredSymbolAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if ($attribute->isRequired() && ! $attribute->isReadOnly()) + echo '*'; + } + + /** BINARY ATTRIBUTE RENDERING **/ + + #@todo do we need a $this->drawJavascriptAttribute($attribute) here too ? + protected function drawJavascriptBinaryAttribute($attribute) { + # If there are no values, then this javascript doesnt need to be drawn. + if (! $attribute->getValues()) + return; + + static $drawn = false; + + # This JS may have been rendered by multiple Binary attributes + if ($drawn) + return; + else + $drawn = true; + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + + echo ''; + echo ''; + printf('',$this->getServerID()); + printf('',$this->template->getDNEncode(false)); + printf('',$this->template->getID()); + echo ''; + echo ''; + echo ''; + + echo ' +'; + echo "\n"; + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + } + + /** DATE ATTRIBUTE RENDERING **/ + + protected function drawJavaScriptDateAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + + $this->drawJavaScriptAttribute($attribute); + + static $drawn = false; + + # This JS may have been rendered by multiple Date attributes + if (! $drawn) { + printf('',JSDIR); + printf('',JSDIR); + printf('',JSDIR); + + $drawn = true; + } + + $config = array(); + $config['date'] = array_change_key_case($_SESSION[APPCONFIG]->getValue('appearance','date_attrs')); + $config['time'] = array_change_key_case($_SESSION[APPCONFIG]->getValue('appearance','date_attrs_showtime')); + $config['format'] = $_SESSION[APPCONFIG]->getValue('appearance','date'); + + if (isset($config['date'][$attribute->getName()])) + $config['format'] = $config['date'][$attribute->getName()]; + + for ($i=0;$i<=$attribute->getValueCount();$i++) { + printf('',$attribute->getName(),$i,$config['format']); + + if (in_array_ignore_case($attribute->getName(),array_keys($config['time'])) && ($config['time'][$attribute->getName()])) + printf('',$attribute->getName(),$i,'true'); + + echo "\n"; + } + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + } + + /** + * Draws an HTML date selector button which, when clicked, pops up a date selector dialog. + */ + protected function drawSelectorPopupDateAttribute($attribute,$i) { + printf('Calendar', + $attribute->getName(),$i,_('Click to popup a dialog to select a date graphically'),IMGDIR,$attribute->getName(),$i); + } + + /** DN ATTRIBUTES **/ + + protected function drawIconDnAttribute($attribute,$val) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + $this->draw('DnValueIcon',$attribute,$val); + } + + /** OBJECT CLASS ATTRIBUTE **/ + + protected function drawIconObjectClassAttribute($attribute,$val) { + return; + } + + /** PASSWORD ATTRIBUTES **/ + + protected function drawJavascriptPasswordAttribute($attribute) { + static $drawn = array(); + + # This JS may have been rendered by multiple Binary attributes + if (isset($drawn[$attribute->getName()]) && $drawn[$attribute->getName()]) + return; + else + $drawn[$attribute->getName()] = true; + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + + $this->drawJavascriptAttribute($attribute); + + # Add the javascript so we can call check password later. + echo ' +'; + echo "\n"; + + printf('',__METHOD__,$attribute->getName()); + echo "\n"; + } + + protected function drawCheckLinkPasswordAttribute($attribute,$component_id) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + printf('%s...
', + $component_id,$attribute->getName(),_('Check password')); + } + + /** RANDOM PASSWORD **/ + + /** + * This will draw the javascript that displays to the user the random password generated + * + * @todo This function doesnt work well if there are more than 1 RandomPasswordAttributes on the form for the same attribute (unlikely situation) + */ + protected function drawJavascriptRandomPasswordAttribute($attribute) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + printf("\n\n",__METHOD__); + $this->drawJavascriptPasswordAttribute($attribute); + + $pwd = password_generate(); + $pwd = str_replace("\\","\\\\",$pwd); + $pwd = str_replace("'","\\'",$pwd); + + printf("\n\n",__METHOD__); + echo ''; + printf("\n\n",__METHOD__); + } + + protected function drawDefaultHelperPasswordAttribute($attribute,$i) { + $id = 'enc'; + + if ($val = $attribute->getValue($i)) + $default = get_enc_type($val); + else + $default = $this->getServer()->getValue('appearance','password_hash'); + + if (! $attribute->getPostValue()) + printf('',$attribute->getName(),$i); + + printf(''; + } + + protected function drawDefaultHelperSambaPasswordAttribute($attribute,$i) { + $id = 'enc'; + + if (! $attribute->getPostValue()) + printf('',$attribute->getName(),$i); + + switch ($attribute->getName()) { + case 'sambalmpassword' : $enc = 'lm'; break; + case 'sambantpassword' : $enc = 'nt'; break; + + default: + return ''; + } + + printf('', + $id,htmlspecialchars($attribute->getName()),$i, + $id,htmlspecialchars($attribute->getName()),$i,$enc); + } + + /** SELECTION ATTRIBUTE RENDERING **/ + + protected function drawIconSelectionAttribute($attribute,$val) { + if (DEBUGTMP) printf('%s
',__METHOD__); + + if (! $attribute->isMultiple() || $attribute->isReadOnly()) + $this->drawIconAttribute($attribute,$val); + } + + protected function getMenuItemSelectionAttribute($attribute,$i) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (DEBUGTMP) printf('%s
',__METHOD__); + + switch ($i) { + case 'add': + if (! $attribute->isMultiple()) + return $this->getMenuItemAttribute($attribute,$i); + else + return ''; + + case 'modify': + return ''; + + default: + return $this->getMenuItemAttribute($attribute,$i); + } + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/Tree.php b/lam/templates/3rdParty/pla/lib/Tree.php new file mode 100644 index 00000000..b3efcf9a --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/Tree.php @@ -0,0 +1,344 @@ +server_id = $server_id; + } + + /** + * Create an instance of the tree - this is used when we call this class directly + * Tree::getInstance($index) + * + * @return object Tree + */ + static public function getInstance($server_id) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $tree = get_cached_item($server_id,'tree'); + + if (! $tree) { + $server = $_SESSION[APPCONFIG]->getServer($server_id); + + if (! $server) + return null; + + $treeclass = $_SESSION[APPCONFIG]->getValue('appearance','tree'); + eval('$tree = new '.$treeclass.'($server_id);'); + + # If we are not logged in, just return the empty tree. + if (is_null($server->getLogin(null))) + return $tree; + + foreach ($server->getBaseDN(null) as $base) + if ($base) + $tree->addEntry($base); + + set_cached_item($server_id,'tree','null',$tree); + } + + return $tree; + } + + /** + * Get the Server ID for this tree + * + * @return int Server ID that this tree is for + */ + protected function getServerID() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->server_id); + + return $this->server_id; + } + + /** + * Get the server Object for this tree + * + * @return object Server Object for this tree + */ + protected function getServer() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $_SESSION[APPCONFIG]->getServer($this->server_id); + } + + /** + * Get the entries that are BaseDN entries. + * + * @return array Base DN entries + */ + public function getBaseEntries() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $return = array(); + + foreach ($this->entries as $details) + if ($details->isBaseDN()) + array_push($return,$details); + + return $return; + } + + /** + * This function will take the DN, convert it to lowercase and strip unnessary + * commas. This result will be used as the index for the tree object. + * Any display of a DN should use the object->dn entry, not the index. + * The reason we need to do this is because: + * uid=User A,ou=People,c=AU and + * uid=User B, ou=PeOpLe, c=au + * are infact in the same branch, but PLA will show them inconsistently. + * + * @param dn DN to clean + * @return dn Lowercase clean DN + */ + private function indexDN($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $index = strtolower(implode(',',pla_explode_dn($dn))); + + if (DEBUG_ENABLED) + debug_log('Result (%s)',1,0,__FILE__,__LINE__,__METHOD__,$index); + + return $index; + } + + /** + * Get a tree entry + * + * @param dn DN to retrieve + * @return object Tree DN object + */ + public function getEntry($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $dnlower = $this->indexDN($dn); + + if (isset($this->entries[$dnlower])) + return $this->entries[$dnlower]; + else + return null; + } + + /** + * Add an entry in the tree view ; the entry is added in the + * children array of its parent + * + * @param dn DN to add + * @param string $dn the dn of the entry to create + */ + public function addEntry($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $dnlower = $this->indexDN($dn); + + # @todo Temporarily removed, some non-ascii char DNs that do exist, fail here for some reason? + #if (! ($server->dnExists($dn))) + # return; + + if (isset($this->entries[$dnlower])) + debug_dump_backtrace('Calling add entry to an entry that ALREADY exists?',1); + + if (DEBUG_ENABLED) + debug_log('New ENTRY (%s).',64,0,__FILE__,__LINE__,__METHOD__,$dn); + + $tree_factory = new TreeItem($server->getIndex(),$dn); + $tree_factory->setObjectClasses($server->getDNAttrValue($dn,'objectClass')); + + if ((($isleaf = $server->getDNAttrValue($dn,'hassubordinates')) && ! strcasecmp($isleaf[0],'false'))) + $tree_factory->setLeaf(); + + $this->entries[$dnlower] = $tree_factory; + + # Is this entry in a base entry? + if (in_array_ignore_case($dn,$server->getBaseDN(null))) { + $this->entries[$dnlower]->setBase(); + + # If the parent entry is not in the tree, we add it. This routine will in itself + # recall this method until we get to the top of the tree (the base). + } else { + $parent_dn = $server->getContainer($dn); + + if (DEBUG_ENABLED) + debug_log('Parent DNs (%s)',64,0,__FILE__,__LINE__,__METHOD__,$parent_dn); + + if ($parent_dn) { + $parent_entry = $this->getEntry($parent_dn); + + if (! $parent_entry) { + $this->addEntry($parent_dn); + $parent_entry = $this->getEntry($parent_dn); + } + + # Update this DN's parent's children list as well. + $parent_entry->addChild($dn); + } + } + } + + /** + * Delete an entry from the tree view ; the entry is deleted from the + * children array of its parent + * + * @param dn DN to remote + */ + public function delEntry($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $dnlower = $this->indexDN($dn); + + if (isset($this->entries[$dnlower])) + unset($this->entries[$dnlower]); + + # Delete entry from parent's children as well. + $parent_dn = $server->getContainer($dn); + $parent_entry = $this->getEntry($parent_dn); + + if ($parent_entry) + $parent_entry->delChild($dn); + } + + /** + * Rename an entry in the tree + * + * @param dn Old DN + * @param dn New DN + */ + public function renameEntry($dnOLD,$dnNEW) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $dnlowerOLD = $this->indexDN($dnOLD); + $dnlowerNEW = $this->indexDN($dnNEW); + + $this->entries[$dnlowerNEW] = $this->entries[$dnlowerOLD]; + if ($dnlowerOLD != $dnlowerNEW) + unset($this->entries[$dnlowerOLD]); + $this->entries[$dnlowerNEW]->rename($dnNEW); + + # Update the parent's children + $parentNEW = $server->getContainer($dnNEW); + $parentOLD = $server->getContainer($dnOLD); + + $parent_entry = $this->getEntry($parentNEW); + if ($parent_entry) + $parent_entry->addChild($dnNEW); + + $parent_entry = $this->getEntry($parentOLD); + if ($parent_entry) + $parent_entry->delChild($dnOLD); + } + + /** + * Read the children of a tree entry + * + * @param dn DN of the entry + * @param boolean LDAP Size Limit + */ + public function readChildren($dn,$nolimit=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $this->getServer(); + $dnlower = $this->indexDN($dn); + + if (! isset($this->entries[$dnlower])) + debug_dump_backtrace('Reading children on an entry that isnt set? '.$dnlower,true); + + $ldap['child_limit'] = $nolimit ? 0 : $_SESSION[APPCONFIG]->getValue('search','size_limit'); + $ldap['filter'] = $_SESSION[APPCONFIG]->getValue('appearance','tree_filter'); + $ldap['deref'] = $_SESSION[APPCONFIG]->getValue('deref','tree'); + + # Perform the query to get the children. + $ldap['children'] = $server->getContainerContents($dn,null,$ldap['child_limit'],$ldap['filter'],$ldap['deref']); + + if (! count($ldap['children'])) { + $this->entries[$dnlower]->unsetSizeLimited(); + + return; + } + + if (DEBUG_ENABLED) + debug_log('Children of (%s) are (%s)',64,0,__FILE__,__LINE__,__METHOD__,$dn,$ldap['children']); + + # Relax our execution time, it might take some time to load this + if ($nolimit) + @set_time_limit($_SESSION[APPCONFIG]->getValue('search','time_limit')); + + $this->entries[$dnlower]->readingChildren(true); + + foreach ($ldap['children'] as $child) { + if (DEBUG_ENABLED) + debug_log('Adding (%s)',64,0,__FILE__,__LINE__,__METHOD__,$child); + + if (! in_array($child,$this->entries[$dnlower]->getChildren())) + $this->entries[$dnlower]->addChild($child); + } + + $this->entries[$dnlower]->readingChildren(false); + + if (count($this->entries[$dnlower]->getChildren()) == $ldap['child_limit']) + $this->entries[$dnlower]->setSizeLimited(); + else + $this->entries[$dnlower]->unsetSizeLimited(); + } + + /** + * Return the number of children an entry has. Optionally autoread the child entry. + * + * @param dn DN of the entry + * @param boolean LDAP Size Limit + */ + protected function readChildrenNumber($dn,$nolimit=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $dnlower = $this->indexDN($dn); + + if (! isset($this->entries[$dnlower])) + debug_dump_backtrace('Reading children on an entry that isnt set?',true); + + # Read the entry if we havent got it yet. + if (! $this->entries[$dnlower]->isLeaf() && ! $this->entries[$dnlower]->getChildren()) + $this->readChildren($dn,$nolimit); + + return count($this->entries[$dnlower]->getChildren()); + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/TreeItem.php b/lam/templates/3rdParty/pla/lib/TreeItem.php new file mode 100644 index 00000000..8c95ad83 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/TreeItem.php @@ -0,0 +1,312 @@ +server_id = $server_id; + $this->dn = $dn; + } + + /** + * Get the DN of this tree item. + * + * @return DN The DN of this item. + */ + public function getDN() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->dn); + + return $this->dn; + } + + public function getDNEncode() { + return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->dn)); + } + + /** + * Get the RDN of this tree items DN. + * + * @return RDN The RDN of this items DN. + */ + public function getRDN() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return get_rdn($this->getDn(),0,true); + } + + /** + * Set this item as a LDAP base DN item. + */ + public function setBase() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->base_entry = true; + } + + /** + * Return if this item is a base DN item. + */ + public function isBaseDN() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->base_entry); + + return $this->base_entry; + } + + public function setObjectClasses($oc) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->objectclasses = $oc; + } + + public function getObjectClasses() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->objectclasses); + + return $this->objectclasses; + } + + public function isInLDAP() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return count($this->objectclasses) ? true : false; + } + + /** + * Returns null if the children have never be defined + * or an array of the dn of the children + */ + public function getChildren() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->children); + + if ($this->childsort && ! $this->reading_children) { + usort($this->children,'pla_compare_dns'); + $this->childsort = false; + } + + return $this->children; + } + + public function readingChildren($bool) { + $this->reading_children = $bool; + } + + /** + * Do the children require resorting + */ + public function isChildSorted() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->childsort); + + return $this->childsort; + } + + /** + * Mark the children as sorted + */ + public function childSorted() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->childsort = false; + } + + /** + * Add a child to this DN entry. + * + * @param DN The DN to add. + */ + public function addChild($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (in_array($dn,$this->children)) + return; + + array_push($this->children,$dn); + $this->childsort = true; + } + + /** + * Delete a child from this DN entry. + * + * @param DN The DN to add. + */ + public function delChild($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->children) { + # If the parent hasnt been opened in the tree, then there wont be any children. + $index = array_search($dn,$this->children); + + if ($index !== false) + unset($this->children[$index]); + } + } + + /** + * Rename this DN. + * + * @param DN The DN to rename to. + */ + public function rename($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->dn = $dn; + } + + /** + * Return if this item has been opened. + */ + public function isOpened() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->open); + + return $this->open; + } + + /** + * Mark this node as closed. + */ + public function close() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->open = false; + } + + /** + * Opens the node ; the children of the node must have been defined + */ + public function open() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->open = true; + } + + /** + * Mark this node as a leaf. + */ + public function setLeaf() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->leaf = true; + } + + /** + * Return if this node is a leaf. + */ + public function isLeaf() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->leaf); + + return $this->leaf; + } + + /** + * Returns the path of the icon file used to represent this node ; + * If the icon hasnt been set, it will call get_icon() + */ + public function getIcon() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->icon); + + if (! $this->icon) + $this->icon = get_icon($this->server_id,$this->dn,$this->objectclasses); + + return $this->icon; + } + + /** + * Mark this node as a size limited (it wont have all its children). + */ + public function setSizeLimited() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->size_limited = true; + } + + /** + * Clear the size limited flag. + */ + public function unsetSizeLimited() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->size_limited = false; + } + + /** + * Return if this node has hit an LDAP size limit (and thus doesnt have all its children). + */ + public function isSizeLimited() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->size_limited; + } + + public function setTemplate($template) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->template = $template; + } + + public function getTemplate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',33,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->template; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/Visitor.php b/lam/templates/3rdParty/pla/lib/Visitor.php new file mode 100644 index 00000000..b9affdbf --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/Visitor.php @@ -0,0 +1,112 @@ +Class (%s): Method doesnt exist (%s,%s)
',$class,get_class($this),$call); + + $class = get_parent_class($class); + $call = "$method$fnct$class"; + array_push($methods,$call); + } + + if (defined('DEBUGTMP') && DEBUGTMP) + printf('Calling Methods: %s
',implode('|',$methods)); + + if (defined('DEBUGTMP') && DEBUGTMP && method_exists($this,$call)) + printf('Method Exists: %s::%s (%s)
',get_class($this),$call,$args); + + if (method_exists($this,$call)) { + $call .= '('; + + for ($i = 0; $i < count($args); $i++) + if ($i == 0) + $call .= sprintf('$args[%s]',$i); + else + $call .= sprintf(',$args[%s]',$i); + + $call .= ');'; + + if (defined('DEBUGTMP') && DEBUGTMP) + printf('Invoking Method: $this->%s
',$call); + + eval('$r = $this->'.$call); + + if (isset($r)) + return $r; + else + return; + + } elseif (DEBUG_ENABLED) { + debug_log('Doesnt exist param (%s,%s)',1,0,__FILE__,__LINE__,__METHOD__,$method,$fnct); + } + + printf('NO Methods: %s
',implode('|',$methods)); + } + + /** + * Return the LDAP server ID + * + * @return int Server ID + */ + public function getServerID() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->server_id); + + if (isset($this->server_id)) + return $this->server_id; + else + return null; + } + + /** + * Return this LDAP Server object + * + * @return object DataStore Server + */ + protected function getServer() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $_SESSION[APPCONFIG]->getServer($this->getServerID()); + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/blowfish.php b/lam/templates/3rdParty/pla/lib/blowfish.php new file mode 100644 index 00000000..1c69aa9a --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/blowfish.php @@ -0,0 +1,480 @@ + + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Mike Cochrane + * @version $Revision$ + * @since Horde 2.2 + * @package horde.cipher + */ +/** + * @package horde.cipher + */ + +// Change for phpMyAdmin by lem9: +//class Horde_Cipher_blowfish extends Horde_Cipher { +class Horde_Cipher_blowfish { + + /* Pi Array */ + public $p = array( + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, + 0x9216D5D9, 0x8979FB1B); + + /* S Boxes */ + public $s1 = array( + 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, + 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, + 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, + 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, + 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, + 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, + 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, + 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, + 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, + 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, + 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, + 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, + 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, + 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, + 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, + 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, + 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, + 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, + 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, + 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, + 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, + 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, + 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, + 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, + 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, + 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, + 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, + 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, + 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, + 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, + 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, + 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, + 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, + 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, + 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, + 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, + 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, + 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, + 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, + 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, + 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, + 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, + 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, + 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, + 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, + 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, + 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, + 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, + 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, + 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, + 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, + 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, + 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, + 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, + 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, + 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, + 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, + 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, + 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, + 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, + 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, + 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, + 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, + 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A); + public $s2 = array( + 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, + 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, + 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, + 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, + 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, + 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, + 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, + 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, + 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, + 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, + 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, + 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, + 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, + 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, + 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, + 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, + 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, + 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, + 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, + 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, + 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, + 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, + 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, + 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, + 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, + 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, + 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, + 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, + 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, + 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, + 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, + 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, + 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, + 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, + 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, + 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, + 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, + 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, + 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, + 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, + 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, + 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, + 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, + 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, + 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, + 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, + 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, + 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, + 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, + 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, + 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, + 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, + 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, + 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, + 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, + 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, + 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, + 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, + 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, + 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, + 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, + 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, + 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, + 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7); + public $s3 = array( + 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, + 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, + 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, + 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, + 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, + 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, + 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, + 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, + 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, + 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, + 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, + 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, + 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, + 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, + 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, + 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, + 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, + 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, + 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, + 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, + 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, + 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, + 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, + 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, + 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, + 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, + 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, + 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, + 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, + 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, + 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, + 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, + 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, + 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, + 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, + 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, + 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, + 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, + 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, + 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, + 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, + 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, + 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, + 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, + 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, + 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, + 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, + 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, + 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, + 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, + 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, + 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, + 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, + 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, + 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, + 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, + 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, + 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, + 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, + 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, + 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, + 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, + 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, + 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0); + public $s4 = array( + 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, + 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, + 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, + 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, + 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, + 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, + 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, + 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, + 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, + 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, + 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, + 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, + 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, + 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, + 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, + 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, + 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, + 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, + 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, + 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, + 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, + 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, + 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, + 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, + 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, + 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, + 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, + 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, + 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, + 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, + 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, + 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, + 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, + 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, + 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, + 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, + 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, + 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, + 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, + 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, + 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, + 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, + 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, + 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, + 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, + 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, + 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, + 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, + 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, + 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, + 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, + 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, + 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, + 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, + 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, + 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, + 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, + 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, + 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, + 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, + 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, + 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, + 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, + 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6); + + /* The number of rounds to do */ + public $_rounds = 16; + + /* Constructor */ + function Cipher_blowfish($params = null) + { + } + + /** + * Set the key to be used for en/decryption + * + * @param String $key The key to use + */ + function setKey($key) + { + $key = $this->_formatKey($key); + $keyPos = $keyXor = 0; + + $iMax = count($this->p); + $keyLen = count($key); + for ($i = 0; $i < $iMax; $i++) { + for ($t = 0; $t < 4; $t++) { + $keyXor = ($keyXor << 8) | (($key[$keyPos]) & 0x0ff); + if (++$keyPos == $keyLen) { + $keyPos = 0; + } + } + $this->p[$i] = $this->p[$i] ^ $keyXor; + } + + $encZero = array('L' => 0, 'R' => 0); + for ($i = 0; $i + 1 < $iMax; $i += 2) { + $encZero = $this->_encryptBlock($encZero['L'], $encZero['R']); + $this->p[$i] = $encZero['L']; + $this->p[$i + 1] = $encZero['R']; + } + + $iMax = count($this->s1); + for ($i = 0; $i < $iMax; $i += 2) { + $encZero = $this->_encryptBlock($encZero['L'], $encZero['R']); + $this->s1[$i] = $encZero['L']; + $this->s1[$i + 1] = $encZero['R']; + } + + $iMax = count($this->s2); + for ($i = 0; $i < $iMax; $i += 2) { + $encZero = $this->_encryptBlock($encZero['L'], $encZero['R']); + $this->s2[$i] = $encZero['L']; + $this->s2[$i + 1] = $encZero['R']; + } + + $iMax = count($this->s3); + for ($i = 0; $i < $iMax; $i += 2) { + $encZero = $this->_encryptBlock($encZero['L'], $encZero['R']); + $this->s3[$i] = $encZero['L']; + $this->s3[$i + 1] = $encZero['R']; + } + + $iMax = count($this->s4); + for ($i = 0; $i < $iMax; $i += 2) { + $encZero = $this->_encryptBlock($encZero['L'], $encZero['R']); + $this->s4[$i] = $encZero['L']; + $this->s4[$i + 1] = $encZero['R']; + } + + } + + /** + * Return the size of the blocks that this cipher needs + * + * @return Integer The number of characters per block + */ + function getBlockSize() + { + return 8; + } + + /** + * Encrypt a block on data. + * + * @param String $block The data to encrypt + * @param optional String $key The key to use + * + * @return String the encrypted output + */ + function encryptBlock($block, $key = null) + { + if (!is_null($key)) { + $this->setKey($key); + } + + list($L, $R) = array_values(unpack('N*', $block)); + $parts = $this->_encryptBlock($L, $R); + return pack("NN", $parts['L'], $parts['R']); + } + + /** + * Encrypt a block on data. + * + * @param String $L The data to encrypt. + * @param String $R The data to encrypt. + * + * @return String The encrypted output. + */ + function _encryptBlock($L, $R) + { + $L ^= $this->p[0]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[1]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[2]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[3]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[4]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[5]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[6]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[7]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[8]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[9]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[10]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[11]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[12]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[13]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[14]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[15]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[16]; + $R ^= $this->p[17]; + + return array('L' => $R, 'R' => $L); + } + + /** + * Decrypt a block on data. + * + * @param String $block The data to decrypt + * @param optional String $key The key to use + * + * @return String the decrypted output + */ + function decryptBlock($block, $key = null) + { + if (!is_null($key)) { + $this->setKey($key); + } + + $unpack = unpack('N*', $block); + + if (! is_array($unpack)) + error( + sprintf('BLOWFISH: decryptBock()
We expected unpack to produce an array, but instead it produced [%s]. This function was entered with (%s,%s). If you think that this is a bug, then please tell the PLA developers how you got here. You are using PLA [%s,%s]', + serialize($unpack),$block,$key,app_version(),phpversion()),'error','index.php'); + + list($L, $R) = array_values($unpack); + + $L ^= $this->p[17]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[16]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[15]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[14]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[13]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[12]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[11]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[10]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[9]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[8]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[7]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[6]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[5]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[4]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[3]; + $R ^= ((($this->s1[($L >> 24) & 0xFF] + $this->s2[($L >> 16) & 0x0ff]) ^ $this->s3[($L >> 8) & 0x0ff]) + $this->s4[$L & 0x0ff]) ^ $this->p[2]; + $L ^= ((($this->s1[($R >> 24) & 0xFF] + $this->s2[($R >> 16) & 0x0ff]) ^ $this->s3[($R >> 8) & 0x0ff]) + $this->s4[$R & 0x0ff]) ^ $this->p[1]; + + $decrypted = pack("NN", $R ^ $this->p[0], $L); + return $decrypted; + } + + /** + * Converts a text key into an array. + * + * @return array The key. + */ + function _formatKey($key) + { + return array_values(unpack('C*', $key)); + } + +} +?> diff --git a/lam/templates/3rdParty/pla/lib/common.php b/lam/templates/3rdParty/pla/lib/common.php new file mode 100644 index 00000000..8304d31f --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/common.php @@ -0,0 +1,266 @@ +getValue('appearance','theme')))) + && is_file(realpath(sprintf('css/%s/%s',$_SESSION[APPCONFIG]->getValue('appearance','theme'),$_SESSION[APPCONFIG]->getValue('appearance','stylesheet'))))) + + $app['theme'] = $_SESSION[APPCONFIG]->getValue('appearance','theme'); + +define('CSSDIR',sprintf('css/%s',$app['theme'])); +define('IMGDIR',sprintf('images/%s',$app['theme'])); + +# Initialise the hooks +if (file_exists(LIBDIR.'hooks.php')) + require_once LIBDIR.'hooks.php'; + +# If we get here, and $_SESSION[APPCONFIG] is not set, then redirect the user to the index. +if (isset($_SERVER['SERVER_SOFTWARE']) && ! isset($_SESSION[APPCONFIG])) { + if ($_SERVER['QUERY_STRING']) + header(sprintf('Location: index.php?URI=%s',base64_encode($_SERVER['QUERY_STRING']))); + else + header('Location: index.php'); + + die(); + +} else { + # SF Bug #1903987 + if (! method_exists($_SESSION[APPCONFIG],'CheckCustom')) + error('Unknown situation, $_SESSION[APPCONFIG] exists, but method CheckCustom() does not','error',null,true,true); + + # Check our custom variables. + # @todo Change this so that we dont process a cached session. + $_SESSION[APPCONFIG]->CheckCustom(); +} + +# Check for safe mode. +if (@ini_get('safe_mode') && ! get_request('cmd','GET')) + system_message(array( + 'title'=>_('PHP Safe Mode'), + 'body'=>_('You have PHP Safe Mode enabled. This application may work unexpectedly in Safe Mode.'), + 'type'=>'info')); + +# Set our timezone, if it is specified in config.php +if ($_SESSION[APPCONFIG]->getValue('appearance','timezone')) + date_default_timezone_set($_SESSION[APPCONFIG]->getValue('appearance','timezone')); + +# If we are here, $_SESSION is set - so enabled DEBUGing if it has been configured. +if (($_SESSION[APPCONFIG]->getValue('debug','syslog') || $_SESSION[APPCONFIG]->getValue('debug','file')) + && $_SESSION[APPCONFIG]->getValue('debug','level')) + define('DEBUG_ENABLED',1); +else + define('DEBUG_ENABLED',0); + +if (DEBUG_ENABLED) + debug_log('Application (%s) initialised and starting with (%s).',1,0,__FILE__,__LINE__,__METHOD__, + app_version(),$_REQUEST); + +# Set our PHP timelimit. +if ($_SESSION[APPCONFIG]->getValue('session','timelimit') && ! @ini_get('safe_mode')) + set_time_limit($_SESSION[APPCONFIG]->getValue('session','timelimit')); + +# If debug mode is set, increase the time_limit, since we probably need it. +if (DEBUG_ENABLED && $_SESSION[APPCONFIG]->getValue('session','timelimit') && ! @ini_get('safe_mode')) + set_time_limit($_SESSION[APPCONFIG]->getValue('session','timelimit') * 5); + +setlanguage(); + +/** + * Strip slashes from GET, POST, and COOKIE variables if this + * PHP install is configured to automatically addslashes() + */ +if (@get_magic_quotes_gpc() && (! isset($slashes_stripped) || ! $slashes_stripped)) { + array_stripslashes($_REQUEST); + array_stripslashes($_GET); + array_stripslashes($_POST); + array_stripslashes($_COOKIE); + $slashes_stripped = true; +} + +# Create our application repository variable. +$app['server'] = $_SESSION[APPCONFIG]->getServer(get_request('server_id','REQUEST')); + +/** + * Look/evaluate our timeout + */ +if (! $app['server']->isSessionValid()) { + system_message(array( + 'title'=>_('Session Timed Out'), + 'body'=>sprintf('%s %s %s', + _('Your Session timed out after'),$app['server']->getValue('login','timeout'), + _('min. of inactivity. You have been automatically logged out.')), + 'type'=>'info'),sprintf('index.php?server_id=%s&refresh=SID_%s',$app['server']->getIndex(),$app['server']->getIndex())); + + die(); +} + +# If syslog is enabled, we need to include the supporting file. +if ($_SESSION[APPCONFIG]->getValue('debug','syslog')) + require LIBDIR.'syslog.php'; + +/** + * At this point we have read all our additional function PHP files and our configuration. + * If we are using hooks, run the session_init hook. + */ +if (function_exists('run_hook')) + run_hook('post_session_init',array()); +?> diff --git a/lam/templates/3rdParty/pla/lib/config_default.php b/lam/templates/3rdParty/pla/lib/config_default.php new file mode 100644 index 00000000..b9633743 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/config_default.php @@ -0,0 +1,803 @@ +custom = new stdClass; + $this->default = new stdClass; + + /* + * What to do after entry creation : + * 2 : display the creation form again + * 1 : display the new created entry + * 0 : display the choice between 1 and 2 + */ + $this->default->appearance['action_after_creation'] = array( + 'desc'=>'Display the new created entry', + 'default'=>1); + + ## Appearance Attributes + /** Anonymous implies read only + * Set to true if you want LDAP data to be displayed read-only (without input fields) + * when a user logs in to a server anonymously + */ + $this->default->appearance['anonymous_bind_implies_read_only'] = array( + 'desc'=>'Display as read only if user logs in with anonymous bind', + 'default'=>true); + + $this->default->appearance['attr_display_order'] = array( + 'desc'=>'Custom order to display attributes', + 'default'=>array()); + + /* + * @todo Compression is not working, + * purge_cache shows blank, + * tree refresh shows blank - and if view_tree_node is modified to compress output, then previously opened items show up as compressed data. + */ + $this->default->appearance['compress'] = array( + 'desc'=>'Compress Output', + 'untested'=>true, + 'default'=>false); + + $this->default->appearance['control_icons'] = array( + 'desc'=>'Show the control as icons or text', + 'default'=>false); + + $this->default->appearance['custom_templates_only'] = array( + 'desc'=>'Only display the custom templates.', + 'default'=>false); + + $this->default->appearance['date'] = array( + 'desc'=>'Date format whenever dates are shown', + 'default'=>'%A %e %B %Y'); + + $this->default->appearance['date_attrs'] = array( + 'desc'=>'Array of attributes that should show a jscalendar', + 'default'=>array('shadowExpire'=>'%es','shadowLastChange'=>'%es')); + + $this->default->appearance['date_attrs_showtime'] = array( + 'desc'=>'Array of attributes that should show a the time when showing the jscalendar', + 'default'=>array('')); + + $this->default->appearance['disable_default_template'] = array( + 'desc'=>'Disabled the Default Template', + 'default'=>false); + + $this->default->appearance['disable_default_leaf'] = array( + 'desc'=>'Disabled creating leaf entries in the Default Template', + 'default'=>false); + + $this->default->appearance['friendly_attrs'] = array( + 'desc'=>'Friendly names for attributes', + 'default'=>array()); + + $this->default->appearance['hide_attrs'] = array( + 'desc'=>'Hide attributes from display', + 'default'=>array()); + + $this->default->appearance['hide_attrs_exempt'] = array( + 'desc'=>'Group DN, where membership will exempt the users from hide_attrs', + 'default'=>null); + + $this->default->appearance['hide_debug_info'] = array( + 'desc'=>'Hide the features that may provide sensitive debugging information to the browser', + 'default'=>true); + + $this->default->appearance['hide_template_regexp'] = array( + 'desc'=>'Templates that are disabled by their regex are not shown', + 'default'=>false); + + $this->default->appearance['hide_template_warning'] = array( + 'desc'=>'Hide template errors from being displayed', + 'default'=>false); + + /** Language + * The language setting. If you set this to 'auto', phpLDAPadmin will + * attempt to determine your language automatically. Otherwise, set + * this to your applicable language in xx_XX format. + * Localization is not complete yet, but most strings have been translated. + * Please help by writing language files. + */ + $this->default->appearance['language'] = array( + 'desc'=>'Language', + 'default'=>'auto'); + + $this->default->appearance['max_add_attrs'] = array( + 'desc'=>'Maximum number of attrs to show in the add attr form', + 'default'=>10); + + /** + * If you want certain attributes to be editable as multi-line, include them in this list + * A multi-line textarea will be drawn instead of a single-line text field + */ + $this->default->appearance['multi_line_attributes'] = array( + 'desc'=>'Attributes to show as multiline attributes', + 'default'=>array('postalAddress','homePostalAddress','personalSignature','description','mailReplyText')); + + /** + * A list of syntax OIDs which support multi-line attribute values: + */ + $this->default->appearance['multi_line_syntax_oids'] = array( + 'desc'=>'Attributes to show as multiline attributes', + 'default'=>array( + // octet string syntax OID: + '1.3.6.1.4.1.1466.115.121.1.40', + // postal address syntax OID: + '1.3.6.1.4.1.1466.115.121.1.41')); + + /** Obfuscate Password + * If true, display all password hash values as "******". Note that clear-text + * passwords will always be displayed as "******", regardless of this setting. + */ + $this->default->appearance['obfuscate_password_display'] = array( + 'desc'=>'Obfuscate the display of passwords', + 'default'=>true); + + $this->default->appearance['page_title'] = array( + 'desc'=>'Change the page title to this text', + 'default'=>''); + + $this->default->appearance['rdn_all_attrs'] = array( + 'desc'=>'Whether to show all attributes in the RDN chooser, or just the required ones', + 'default'=>true); + + $this->default->appearance['readonly_attrs'] = array( + 'desc'=>'Mark these attributes as readonly', + 'default'=>array()); + + $this->default->appearance['readonly_attrs_exempt'] = array( + 'desc'=>'Group DN, where membership will exempt the users from readonly attrs', + 'default'=>null); + + $this->default->appearance['remoteurls'] = array( + 'desc'=>'Whether to include renders for remote URLs', + 'default'=>true); + + $this->default->appearance['show_clear_password'] = array( + 'desc'=>'Whether to show clear passwords if we dont obfuscate them', + 'default'=>false); + + $this->default->appearance['show_hints'] = array( + 'desc'=>'Show helpful hints', + 'default'=>true); + + $this->default->appearance['show_top_create'] = array( + 'desc'=>'Show a additional create link on the top of the list if there are more than 10 entries', + 'default'=>true); + + $this->default->appearance['show_schema_link'] = array( + 'desc'=>'Show the schema link for each attribute', + 'default'=>true); + + $this->default->appearance['show_attribute_notes'] = array( + 'desc'=>'Show notes for each attribute', + 'default'=>true); + + $this->default->appearance['stylesheet'] = array( + 'desc'=>'Style sheet to use', + 'default'=>'style.css'); + + $this->default->appearance['theme'] = array( + 'desc'=>'Which theme to use', + 'default'=>'default'); + + $this->default->appearance['timezone'] = array( + 'desc'=>'Define our timezone, if not defined in php.ini', + 'default'=>null); + + $this->default->appearance['tree'] = array( + 'desc'=>'Class name which inherits from Tree class and implements the draw() method', + 'default'=>'AJAXTree'); + + /** Tree display + * An array of format strings used to display enties in the + * tree viewer (left-hand side). The first format string that + * is completely defined (i.e., does not reference attributes + * that are not defined the object). If there is no format + * string that is completely defined, the last one is used. + * + * You can use special tokens to draw the entries as you wish. + * You can even mix in HTML to format the string. + * Here are all the tokens you can use: + * %rdn - draw the RDN of the entry (ie, "cn=Dave") + * %dn - draw the DN of the entry (ie, "cn=Dave,ou=People,dc=example,dc=com" + * %rdnValue - draw the value of the RDN (ie, instead of "cn=Dave", just draw "Dave") + * %[attrname]- draw the value (or values) of the specified attribute. + * example: %gidNumber + * + * Any multivalued attributes will be displayed as a comma separated list. + * + * Examples: + * + * To draw the gidNumber and uidNumber to the right of the RDN in a small, gray font: + * '%rdn ( %gidNumber / %uidNumber )' + * To draw the full DN of each entry: + * '%dn' + * To draw the objectClasses to the right in parenthesis: + * '%rdn ( %objectClass )' + * To draw the user-friendly RDN value (ie, instead of "cn=Dave", just draw "Dave"): + * '%rdnValue' + */ + $this->default->appearance['tree_display_format'] = array( + 'desc'=>'LDAP attribute to show in the tree', + 'default'=>array('%rdn')); + + $this->default->appearance['tree_height'] = array( + 'desc'=>'Pixel height of the tree browser', + 'default'=>null); + + $this->default->appearance['tree_width'] = array( + 'desc'=>'Pixel width of the tree browser', + 'default'=>null); + + /** Tree display filter + * LDAP filter used to search entries for the tree viewer (left-hand side) + */ + $this->default->appearance['tree_filter'] = array( + 'desc'=>'LDAP search filter for the tree entries', + 'default'=>'(objectClass=*)'); + + ## Caching + $this->default->cache['schema'] = array( + 'desc'=>'Cache Schema Activity', + 'default'=>true); + + $this->default->cache['query'] = array( + 'desc'=>'Cache Query Configuration', + 'default'=>true); + + $this->default->cache['query_time'] = array( + 'desc'=>'Cache the query configuration for atleast this amount of time in seconds', + 'default'=>5); + + $this->default->cache['template'] = array( + 'desc'=>'Cache Template Configuration', + 'default'=>true); + + $this->default->cache['template_time'] = array( + 'desc'=>'Cache the template configuration for atleast this amount of time in seconds', + 'default'=>60); + + $this->default->cache['tree'] = array( + 'desc'=>'Cache Browser Tree', + 'default'=>true); + + /** Confirm actions + */ + $this->default->confirm['copy'] = array( + 'desc'=>'Confirm copy actions', + 'default'=>true); + + $this->default->confirm['create'] = array( + 'desc'=>'Confirm creation actions', + 'default'=>true); + + $this->default->confirm['update'] = array( + 'desc'=>'Confirm update actions', + 'default'=>true); + + /** Commands + * Define command availability ; if the value of a command is true, + * the command will be available. + */ + $this->default->commands['cmd'] = array( + 'desc'=>'Define command availability', + 'default'=> array( + 'entry_internal_attributes_show' => true, + 'entry_refresh' => true, + 'oslinks' => true, + 'switch_template' => true + )); + + $this->default->commands['script'] = array( + 'desc'=>'Define scripts availability', + 'default'=> array( + 'add_attr_form' => true, + 'add_oclass_form' => true, + 'add_value_form' => true, + 'collapse' => true, + 'compare' => true, + 'compare_form' => true, + 'copy' => true, + 'copy_form' => true, + 'create' => true, + 'create_confirm' => true, + 'delete' => true, + 'delete_attr' => true, + 'delete_form' => true, + 'draw_tree_node' => true, + 'expand' => true, + 'export' => true, + 'export_form' => true, + 'import' => true, + 'import_form' => true, + 'login' => true, + 'logout' => true, + 'login_form' => true, + 'mass_delete' => true, + 'mass_edit' => true, + 'mass_update' => true, + 'modify_member_form' => true, + 'monitor' => true, + 'purge_cache' => true, + 'query_engine' => true, + 'rename' => true, + 'rename_form' => true, + 'rdelete' => true, + 'refresh' => true, + 'schema' => true, + 'server_info' => true, + 'show_cache' => true, + 'template_engine' => true, + 'update_confirm' => true, + 'update' => true + )); + + /** Aliases and Referrrals + * Similar to ldapsearch's -a option, the following options allow you to configure + * how phpLDAPadmin will treat aliases and referrals in the LDAP tree. + * For the following four settings, avaialable options include: + * + * LDAP_DEREF_NEVER - aliases are never dereferenced (eg, the contents of + * the alias itself are shown and not the referenced entry). + * LDAP_DEREF_SEARCHING - aliases should be dereferenced during the search but + * not when locating the base object of the search. + * LDAP_DEREF_FINDING - aliases should be dereferenced when locating the base + * object but not during the search. + * LDAP_DEREF_ALWAYS - aliases should be dereferenced always (eg, the contents + * of the referenced entry is shown and not the aliasing entry) + * We superceed these definitions with @ to suppress the error if php-ldap is + * not installed. + */ + @$this->default->deref['export'] = array( + 'desc'=>'', + 'default'=>LDAP_DEREF_NEVER); + + @$this->default->deref['search'] = array( + 'desc'=>'', + 'default'=>LDAP_DEREF_ALWAYS); + + @$this->default->deref['tree'] = array( + 'desc'=>'', + 'default'=>LDAP_DEREF_NEVER); + + @$this->default->deref['view'] = array( + 'desc'=>'', + 'default'=>LDAP_DEREF_NEVER); + + ## Debug Attributes + $this->default->debug['level'] = array( + 'desc'=>'Debug level verbosity', + 'default'=>0); + + $this->default->debug['syslog'] = array( + 'desc'=>'Whether to send debug messages to syslog', + 'default'=>false); + + $this->default->debug['file'] = array( + 'desc'=>'Name of file to send debug output to', + 'default'=>null); + + $this->default->debug['addr'] = array( + 'desc'=>'IP address of client to provide debugging info.', + 'default'=>null); + + $this->default->debug['append'] = array( + 'desc'=>'Whether to append to the debug file, or create it fresh each time', + 'default'=>true); + + ## Temp Directories + /** JPEG TMPDir + * This directory must be readable and writable by your web server + */ + $this->default->jpeg['tmpdir'] = array( + 'desc'=>'Temporary directory for jpegPhoto data', + 'default'=>'/tmp'); + + ## Mass update commands + $this->default->mass['enabled'] = array( + 'desc'=>'Are mass update commands enabled', + 'default'=>true); + + ## Modify members feature + /** + * Search filter setting for new members. This is used to search possible members that can be added + * to the group. See modify_member_form.php + */ + $this->default->modify_member['filter'] = array( + 'desc'=>'Search filter for member searches', + 'default'=>'(objectclass=Person)'); + + /** + * Group attributes. When these attributes are seen in template_engine.php, add "modify group members" + * link to the attribute + * See template_engine.php + */ + $this->default->modify_member['groupattr'] = array( + 'desc'=>'Group member attributes', + 'default'=>array('member','uniqueMember','memberUid')); + + /** + * Attribute that is added to the group member attribute. For groupOfNames or groupOfUniqueNames this is dn, + * for posixGroup it's uid. See modify_member_form.php + */ + $this->default->modify_member['attr'] = array( + 'desc'=>'Default attribute that is added to the group member attribute', + 'default'=>'dn'); + + /** + * Attribute that is added to the group member attribute. + * For posixGroup it's uid. See modify_member_form.php + */ + $this->default->modify_member['posixattr'] = array( + 'desc'=>'Contents of the group member attribute', + 'default'=>'uid'); + + /** + * Search filter setting for new members to group. This is used to search possible members that can be added + * to the posixGroup. See modify_member_form.php + */ + $this->default->modify_member['posixfilter'] = array( + 'desc'=>'Search filter for posixmember searches', + 'default'=>'(uid=*)'); + + /** + * posixGroup attribute. When this attribute are seen in modify_member_form.php, only posixGroup members are shown + * See modify_member_form.php + */ + $this->default->modify_member['posixgroupattr'] = array( + 'desc'=>'posixGroup member attribute', + 'default'=>'memberUid'); + + ## Session Attributes + /** Cookie Encryption + * phpLDAPadmin can encrypt the content of sensitive cookies if you set this to a big random string. + */ + $this->default->session['blowfish'] = array( + 'desc'=>'Blowfish key to encrypt cookie details', + 'default'=>null); + + /** Cookie Time + * If you used auth_type 'form' in the servers list, you can adjust how long the cookie will last + * (default is 0 seconds, which expires when you close the browser) + */ + $this->default->session['cookie_time'] = array( + 'desc'=>'Time in seconds for the life of cookies', + 'default'=>0); + + $this->default->session['http_realm'] = array( + 'desc'=>'HTTP Authentication Realm', + 'default'=>sprintf('%s %s',app_name(),_('login'))); + + $this->default->session['memorylimit'] = array( + 'desc'=>'Set the PHP memorylimit warning threshold.', + 'default'=>24); + + $this->default->session['timelimit'] = array( + 'desc'=>'Set the PHP timelimit.', + 'default'=>30); + + /** + * Session Menu + */ + $this->default->menu['session'] = array( + 'desc'=>'Menu items when logged in.', + 'default'=>array( + 'schema'=>true, + 'search'=>true, + 'refresh'=>true, + 'server_info'=>true, + 'monitor'=>true, + 'import'=>true, + 'export'=>true + )); + + ## Password Generation + $this->default->password['length'] = array( + 'desc'=>'Length of autogenerated password', + 'default'=>8); + + $this->default->password['numbers'] = array( + 'desc'=>'Number of numbers required in the password', + 'default'=>2); + + $this->default->password['lowercase'] = array( + 'desc'=>'Number of lowercase letters required in the password', + 'default'=>2); + + $this->default->password['uppercase'] = array( + 'desc'=>'Number of uppercase letters required in the password', + 'default'=>2); + + $this->default->password['punctuation'] = array( + 'desc'=>'Number of punctuation letters required in the password', + 'default'=>2); + + $this->default->password['use_similar'] = array( + 'desc'=>'Whether to use similiar characters', + 'default'=>true); + + $this->default->password['no_random_crypt_salt'] = array( + 'desc'=>'Disable random salt for crypt()', + 'default'=>false); + + /** Search display + * By default, when searching you may display a list or a table of results. + * Set this to 'table' to see table formatted results. + * Set this to 'list' to see "Google" style formatted search results. + */ + $this->default->search['display'] = array( + 'desc'=>'Display a list or table of search results', + 'default'=>'list'); + + $this->default->search['size_limit'] = array( + 'desc'=>'Limit the size of searchs on the search page', + 'default'=>50); + + /** + * The list of attributes to display in each search result entry. + * Note that you can add * to the list to display all attributes + */ + $this->default->search['result_attributes'] = array( + 'desc'=>'List of attributes to display in each search result entry', + 'default'=>array('cn','sn','uid','postalAddress','telephoneNumber')); + + $this->default->search['time_limit'] = array( + 'desc'=>'Maximum time to allow unlimited size_limit searches to the ldap server', + 'default'=>120); + } + + /** + * Access the configuration, taking into account the defaults and the customisations + */ + private function getConfigArray($usecache=true) { + static $CACHE = array(); + + if ($usecache && count($CACHE)) + return $CACHE; + + foreach ($this->default as $key => $vals) + $CACHE[$key] = $vals; + + foreach ($this->custom as $key => $vals) + foreach ($vals as $index => $val) + $CACHE[$key][$index]['value'] = $val; + + return $CACHE; + } + + /** + * Get a configuration value. + */ + public function getValue($key,$index,$fatal=true) { + $config = $this->getConfigArray(); + + if (! isset($config[$key])) + if ($fatal) + error(sprintf('A call was made in [%s] to getValue requesting [%s] that isnt predefined.', + basename($_SERVER['PHP_SELF']),$key),'error',null,true); + else + return ''; + + if (! isset($config[$key][$index])) + if ($fatal) + error(sprintf('Requesting an index [%s] in key [%s] that isnt predefined.',$index,$key),'error',null,true); + else + return ''; + + return isset($config[$key][$index]['value']) ? $config[$key][$index]['value'] : $config[$key][$index]['default']; + } + + /** + * Return the untested config items + */ + public function untested() { + $result = array(); + + foreach ($this->default as $option => $details) + foreach ($details as $param => $values) + if (isset($values['untested']) && $values['untested']) + array_push($result,sprintf('%s.%s',$option,$param)); + + return $result; + } + + /** + * Function to check and warn about any unusual defined variables. + */ + public function CheckCustom() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($this->custom)) { + foreach ($this->custom as $masterkey => $masterdetails) { + + if (isset($this->default->$masterkey)) { + + if (! is_array($masterdetails)) + error(sprintf('Error in configuration file, [%s] should be an ARRAY.',$masterdetails),'error',null,true); + + foreach ($masterdetails as $key => $value) { + # Test that the key is correct. + if (! in_array($key,array_keys($this->default->$masterkey))) + error(sprintf('Error in configuration file, [%s] has not been defined as a configurable variable.',$key),'error',null,true); + + # Test if its should be an array or not. + if (is_array($this->default->{$masterkey}[$key]['default']) && ! is_array($value)) + error(sprintf('Error in configuration file, %s[\'%s\'] SHOULD be an array of values.',$masterkey,$key),'error',null,true); + + if (! is_array($this->default->{$masterkey}[$key]['default']) && is_array($value)) + error(sprintf('Error in configuration file, %s[\'%s\'] should NOT be an array of values.',$masterkey,$key),'error',null,true); + } + + } else { + error(sprintf('Error in configuration file, [%s] has not been defined as a MASTER configurable variable.',$masterkey),'error',null,true); + } + } + } + } + + /** + * Get a list of available commands. + */ + public function getCommandList() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $config = $this->getConfigArray(false); + + masort($config['command'],'summary'); + + if (isset($config['command']) && is_array($config['command'])) + return $config['command']; + else + return array(); + } + + /** + * Simple ACL to see if commands can be run + */ + public function isCommandAvailable($index='cmd') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $a = func_get_args(); + array_shift($a); + $a = $a[0]; + + # Command availability list + $cmd = $this->getValue('commands',$index); + + if (! is_string($a) || ! isset($cmd[$a])) + return false; + else + return $cmd[$a]; + } + + public function configDefinition($key,$index,$config) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_array($config) || ! array_key_exists('desc',$config) || ! array_key_exists('default',$config)) + return; + + if (isset($this->default->$key)) + $definition = $this->default->$key; + + $definition[$index] = $config; + $this->default->$key = $definition; + } + + /** + * Return the friendly attributes names + */ + private function getFriendlyAttrs() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return array_change_key_case($this->getValue('appearance','friendly_attrs')); + } + + /** + * This function will return the friendly name of an attribute, if it exists. + * If the friendly name doesnt exist, the attribute name will be returned. + * + * @param attribute + * @return string friendly name|attribute + */ + public function getFriendlyName($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + static $friendly_attrs; + + if (! $friendly_attrs) + $friendly_attrs = $this->getFriendlyAttrs(); + + if (! is_object($attr)) + if (isset($friendly_attrs[$attr])) + return $friendly_attrs[$attr]; + else + return $attr; + + if (isset($friendly_attrs[$attr->getName()])) + return $friendly_attrs[$attr->getName()]; + else + return $attr->getName(false); + } + + /** + * This function will return true if a friendly name exists for an attribute. + * If the friendly name doesnt exist, it will return false. + * + * @param attribute + * @return boolean true|false + */ + public function haveFriendlyName($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $attr->getName(false) != $this->getFriendlyName($attr); + } + + /** + * This function will return the html for a friendly name attribute. + * + * @param attribute + * @return string html for the friendly name. + */ + public function getFriendlyHTML($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->haveFriendlyName($attr)) + return sprintf('%s', + _('Alias for'),$attr->getName(false),$this->getFriendlyName($attr)); + else + return $attr->getName(false); + } + + public function setServers($servers) { + $this->servers = $servers; + } + + public function getServer($index=null) { + return $this->servers->Instance($index); + } + + /** + * Return a list of our servers + * @param boolean $visible - Only return visible servers + */ + public function getServerList($visible=true) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',3,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->servers->getServerList($visible); + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/createlm.php b/lam/templates/3rdParty/pla/lib/createlm.php new file mode 100644 index 00000000..3dc4bb18 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/createlm.php @@ -0,0 +1,381 @@ +permute($key, $this->perm1, 56); + + $c = array(); + $d = array(); + for ($i = 0; $i < 28; $i++) { + $c[$i] = $pk1[$i]; + $d[$i] = $pk1[28 + $i]; + } + + for ($i = 0; $i < 16; $i++) { + $c = $this->lshift($this->sc[$i], $c); + $d = $this->lshift($this->sc[$i], $d); + + $cd = $c; + for ($k = 0; $k < sizeof($d); $k++) $cd[] = $d[$k]; + $ki[$i] = $this->permute($cd, $this->perm2, 48); + } + + $pd1 = $this->permute($in, $this->perm3, 64); + + $l = array(); + $r = array(); + for ($i = 0; $i < 32; $i++) { + $l[$i] = $pd1[$i]; + $r[$i] = $pd1[32 + $i]; + } + + for ($i = 0; $i < 16; $i++) { + $er = $this->permute($r, $this->perm4, 48); + if ($forw) $erk = $this->mxor($er, $ki[$i]); + else $erk = $this->mxor($er, $ki[15 - $i]); + + for ($j = 0; $j < 8; $j++) { + for ($k = 0; $k < 6; $k++) { + $b[$j][$k] = $erk[($j * 6) + $k]; + } + } + for ($j = 0; $j < 8; $j++) { + $m = array(); + $n = array(); + $m = ($b[$j][0] << 1) | $b[$j][5]; + $n = ($b[$j][1] << 3) | ($b[$j][2] << 2) | ($b[$j][3] << 1) | $b[$j][4]; + + for ($k = 0; $k < 4; $k++) { + $b[$j][$k]=($this->sbox[$j][$m][$n] & (1 << (3-$k)))?1:0; + } + } + + for ($j = 0; $j < 8; $j++) { + for ($k = 0; $k < 4; $k++) { + $cb[($j * 4) + $k] = $b[$j][$k]; + } + } + $pcb = $this->permute($cb, $this->perm5, 32); + $r2 = $this->mxor($l, $pcb); + for ($k = 0; $k < 32; $k++) $l[$k] = $r[$k]; + for ($k = 0; $k < 32; $k++) $r[$k] = $r2[$k]; + } + $rl = $r; + for ($i = 0; $i < sizeof($l); $i++) $rl[] = $l[$i]; + return $this->permute($rl, $this->perm6, 64); + } + + /** + * str_to_key + * + * @param string $str + * @return string key + */ + private function str_to_key($str) { + $key[0] = $this->unsigned_shift_r($str[0], 1); + $key[1] = (($str[0]&0x01)<<6) | $this->unsigned_shift_r($str[1], 2); + $key[2] = (($str[1]&0x03)<<5) | $this->unsigned_shift_r($str[2], 3); + $key[3] = (($str[2]&0x07)<<4) | $this->unsigned_shift_r($str[3], 4); + $key[4] = (($str[3]&0x0F)<<3) | $this->unsigned_shift_r($str[4], 5); + $key[5] = (($str[4]&0x1F)<<2) | $this->unsigned_shift_r($str[5], 6); + $key[6] = (($str[5]&0x3F)<<1) | $this->unsigned_shift_r($str[6], 7); + $key[7] = $str[6]&0x7F; + for ($i = 0; $i < 8; $i++) { + $key[$i] = ($key[$i] << 1); + } + return $key; + } + + /** + * smb_hash + * + * @param unknown_type $in + * @param unknown_type $key + * @param unknown_type $forw + * @return unknown + */ + private function smb_hash($in, $key, $forw){ + $key2 = $this->str_to_key($key); + + for ($i = 0; $i < 64; $i++) { + $inb[$i] = ($in[$i/8] & (1<<(7-($i%8)))) ? 1:0; + $keyb[$i] = ($key2[$i/8] & (1<<(7-($i%8)))) ? 1:0; + $outb[$i] = 0; + } + $outb = $this->doHash($inb, $keyb, $forw); + for ($i = 0; $i < 8; $i++) { + $out[$i] = 0; + } + for ($i = 0; $i < 64; $i++) { + if ( $outb[$i] ) { + $out[$i/8] |= (1<<(7-($i%8))); + } + } + return $out; + } + + /** + * E_P16 + * + * @param unknown_type $in + * @return unknown + */ + private function E_P16($in) { + $p14 = array_values(unpack("C*",$in)); + $sp8 = array(0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25); + $p14_1 = array(); + $p14_2 = array(); + for ($i = 0; $i < 7; $i++) { + $p14_1[$i] = $p14[$i]; + $p14_2[$i] = $p14[$i + 7]; + } + $p16_1 = $this->smb_hash($sp8, $p14_1, true); + $p16_2 = $this->smb_hash($sp8, $p14_2, true); + $p16 = $p16_1; + for ($i = 0; $i < sizeof($p16_2); $i++) { + $p16[] = $p16_2[$i]; + } + return $p16; + } + + /** + * Calculates the LM hash of a given password. + * + * @param string $password password + * @return string hash value + */ + public function lmhash($password = "") { + $password = strtoupper($password); + $password = substr($password,0,14); + $password = str_pad($password, 14, chr(0)); + $p16 = $this->E_P16($password); + for ($i = 0; $i < sizeof($p16); $i++) { + $p16[$i] = sprintf("%02X", $p16[$i]); + } + return join("", $p16); + } + + /** + * Calculates the NT hash of a given password. + * + * @param string $password password + * @return string hash value + */ + public function nthash($password = "") { + if (function_exists('mhash')) + if (defined('MHASH_MD4')) + return strtoupper(bin2hex(mhash(MHASH_MD4,iconv('UTF-8','UTF-16LE',$password)))); + else + return strtoupper(hash('md4', iconv("UTF-8","UTF-16LE",$password))); + else + error(_('Your PHP install does not have the mhash() function. Cannot do hashes.'),'error','index.php'); + } + + /** + * Unsigned shift operation for 32bit values. + * + * PHP 4 only supports signed shifts by default. + */ + private function unsigned_shift_r($a, $b) { + $z = 0x80000000; + if ($z & $a) { + $a = ($a >> 1); + $a &= (~$z); + $a |= 0x40000000; + $a = ($a >> ($b - 1)); + } + else { + $a = ($a >> $b); + } + return $a; + } + +} + +?> diff --git a/lam/templates/3rdParty/pla/lib/ds.php b/lam/templates/3rdParty/pla/lib/ds.php new file mode 100644 index 00000000..d5f0e041 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/ds.php @@ -0,0 +1,726 @@ +', any custom extra connection to ds. + */ + abstract public function login($user=null,$pass=null,$method=null); + + /** + * Query the datasource + */ + abstract public function query($query,$method,$index=null,$debug=false); + + /** + * Return error details from previous operation + */ + abstract protected function getErrorMessage(); + abstract protected function getErrorNum(); + + /** + * Functions that set and verify object configuration details + */ + public function setDefaults($defaults) { + foreach ($defaults as $key => $details) + foreach ($details as $setting => $value) + $this->default->{$key}[$setting] = $value; + } + + public function isDefaultKey($key) { + return isset($this->default->$key); + } + + public function isDefaultSetting($key,$setting) { + return array_key_exists($setting,$this->default->{$key}); + } + + /** + * Return a configuration value + */ + public function getValue($key,$setting,$fatal=true) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,1,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($this->custom->{$key}[$setting])) + return $this->custom->{$key}[$setting]; + + elseif (isset($this->default->{$key}[$setting]) && array_key_exists('default',$this->default->{$key}[$setting])) + return $this->default->{$key}[$setting]['default']; + + elseif ($fatal) + debug_dump_backtrace("Error trying to get a non-existant value ($key,$setting)",1); + + else + return null; + } + + /** + * Set a configuration value + */ + public function setValue($key,$setting,$value) { + if (isset($this->custom->{$key}[$setting])) + system_message(array( + 'title'=>_('Configuration setting already defined.'), + 'body'=>sprintf('A call has been made to reset a configuration value (%s,%s,%s)', + $key,$setting,$value), + 'type'=>'info')); + + $this->custom->{$key}[$setting] = $value; + } + + /** + * Return the untested config items + */ + public function untested() { + $result = array(); + + foreach ($this->default as $option => $details) + foreach ($details as $param => $values) + if (isset($values['untested']) && $values['untested']) + array_push($result,sprintf('%s.%s',$option,$param)); + + return $result; + } + + /** + * Get the name of this datastore + */ + public function getName() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->getValue('server','name'); + } + + /** + * Functions that enable login and logout of the application + */ + /** + * Return the authentication type for this object + */ + public function getAuthType() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + switch ($this->getValue('login','auth_type')) { + case 'cookie': + case 'config': + case 'http': + case 'proxy': + case 'session': + case 'sasl': + return $this->getValue('login','auth_type'); + + default: + die(sprintf('Error: %s hasnt been configured for auth_type %s',__METHOD__, + $this->getValue('login','auth_type'))); + } + } + + /** + * Get the login name of the user logged into this datastore's connection method + * If this returns null, we are not logged in. + * If this returns '', we are logged in with anonymous + */ + public function getLogin($method=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $method = $this->getMethod($method); + + # For anonymous binds + if ($method == 'anon') + if (isset($_SESSION['USER'][$this->index][$method]['name'])) + return ''; + else + return null; + + switch ($this->getAuthType()) { + case 'cookie': + if (! isset($_COOKIE[$method.'-USER'])) + # If our bind_id is set, we'll pass that back for logins. + return (! is_null($this->getValue('login','bind_id')) && $method == 'login') ? $this->getValue('login','bind_id') : null; + else + return blowfish_decrypt($_COOKIE[$method.'-USER']); + + case 'config': + if (! isset($_SESSION['USER'][$this->index][$method]['name'])) + return $this->getValue('login','bind_id'); + else + return blowfish_decrypt($_SESSION['USER'][$this->index][$method]['name']); + + case 'proxy': + if (! isset($_SESSION['USER'][$this->index][$method]['proxy'])) + return $this->getValue('login','bind_id'); + else + return blowfish_decrypt($_SESSION['USER'][$this->index][$method]['proxy']); + + case 'http': + case 'session': + case 'sasl': + if (! isset($_SESSION['USER'][$this->index][$method]['name'])) + # If our bind_id is set, we'll pass that back for logins. + return (! is_null($this->getValue('login','bind_id')) && $method == 'login') ? $this->getValue('login','bind_id') : null; + else + return blowfish_decrypt($_SESSION['USER'][$this->index][$method]['name']); + + default: + die(sprintf('Error: %s hasnt been configured for auth_type %s',__METHOD__,$this->getAuthType())); + } + } + + /** + * Set the login details of the user logged into this datastore's connection method + */ + protected function setLogin($user,$pass,$method=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $method = $this->getMethod($method); + + switch ($this->getAuthType()) { + case 'cookie': + set_cookie($method.'-USER',blowfish_encrypt($user),NULL,'/'); + set_cookie($method.'-PASS',blowfish_encrypt($pass),NULL,'/'); + return true; + + case 'config': + return true; + + case 'proxy': + if (isset($_SESSION['USER'][$this->index][$method]['proxy'])) + unset($_SESSION['USER'][$this->index][$method]['proxy']); + + case 'http': + case 'session': + case 'sasl': + $_SESSION['USER'][$this->index][$method]['name'] = blowfish_encrypt($user); + $_SESSION['USER'][$this->index][$method]['pass'] = blowfish_encrypt($pass); + + return true; + + default: + die(sprintf('Error: %s hasnt been configured for auth_type %s',__METHOD__,$this->getAuthType())); + } + } + + /** + * Get the login password of the user logged into this datastore's connection method + */ + protected function getPassword($method=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $method = $this->getMethod($method); + + # For anonymous binds + if ($method == 'anon') + if (isset($_SESSION['USER'][$this->index][$method]['name'])) + return ''; + else + return null; + + switch ($this->getAuthType()) { + case 'cookie': + if (! isset($_COOKIE[$method.'-PASS'])) + # If our bind_id is set, we'll pass that back for logins. + return (! is_null($this->getValue('login','bind_pass')) && $method == 'login') ? $this->getValue('login','bind_pass') : null; + else + return blowfish_decrypt($_COOKIE[$method.'-PASS']); + + case 'config': + case 'proxy': + if (! isset($_SESSION['USER'][$this->index][$method]['pass'])) + return $this->getValue('login','bind_pass'); + else + return blowfish_decrypt($_SESSION['USER'][$this->index][$method]['pass']); + + case 'http': + case 'session': + case 'sasl': + if (! isset($_SESSION['USER'][$this->index][$method]['pass'])) + # If our bind_pass is set, we'll pass that back for logins. + return (! is_null($this->getValue('login','bind_pass')) && $method == 'login') ? $this->getValue('login','bind_pass') : null; + else + return blowfish_decrypt($_SESSION['USER'][$this->index][$method]['pass']); + + default: + die(sprintf('Error: %s hasnt been configured for auth_type %s',__METHOD__,$this->getAuthType())); + } + } + + /** + * Return if this datastore's connection method has been logged into + */ + public function isLoggedIn($method=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + static $CACHE = array(); + + $method = $this->getMethod($method); + + if (isset($CACHE[$this->index][$method]) && ! is_null($CACHE[$this->index][$method])) + return $CACHE[$this->index][$method]; + + $CACHE[$this->index][$method] = null; + + # For some authentication types, we need to do the login here + switch ($this->getAuthType()) { + case 'config': + if (! $CACHE[$this->index][$method] = $this->login($this->getLogin($method),$this->getPassword($method),$method)) + system_message(array( + 'title'=>_('Unable to login.'), + 'body'=>_('Your configuration file has authentication set to CONFIG based authentication, however, the userid/password failed to login'), + 'type'=>'error')); + + break; + + case 'http': + # If our auth vars are not set, throw up a login box. + if (! isset($_SERVER['PHP_AUTH_USER'])) { + # If this server is not in focus, skip the basic auth prompt. + if (get_request('server_id','REQUEST') != $this->getIndex()) { + $CACHE[$this->index][$method] = false; + break; + } + + header(sprintf('WWW-Authenticate: Basic realm="%s %s"',app_name(),_('login'))); + + if ($_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0') + header('HTTP/1.0 401 Unauthorized'); // http 1.0 method + else + header('Status: 401 Unauthorized'); // http 1.1 method + + # If we still dont have login details... + if (! isset($_SERVER['PHP_AUTH_USER'])) { + system_message(array( + 'title'=>_('Unable to login.'), + 'body'=>_('Your configuration file has authentication set to HTTP based authentication, however, there was none presented'), + 'type'=>'error')); + + $CACHE[$this->index][$method] = false; + } + + # Check our auth vars are valid. + } else { + if (! $this->login($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW'],$method)) { + system_message(array( + 'title'=>_('Unable to login.'), + 'body'=>_('Your HTTP based authentication is not accepted by the LDAP server'), + 'type'=>'error')); + + $CACHE[$this->index][$method] = false; + + } else + $CACHE[$this->index][$method] = true; + } + + break; + + case 'proxy': + $CACHE[$this->index][$method] = $this->login($this->getValue('login','bind_id'),$this->getValue('login','bind_pass'),$method); + + break; + + case 'sasl': + # Propogate any given Kerberos credential cache location + if (isset($_ENV['REDIRECT_KRB5CCNAME'])) + putenv(sprintf('KRB5CCNAME=%s',$_ENV['REDIRECT_KRB5CCNAME'])); + elseif (isset($_SERVER['KRB5CCNAME'])) + putenv(sprintf('KRB5CCNAME=%s',$_SERVER['KRB5CCNAME'])); + + # Map the SASL auth ID to a DN + $regex = $this->getValue('login', 'sasl_dn_regex'); + $replacement = $this->getValue('login', 'sasl_dn_replacement'); + + if ($regex && $replacement) { + $userDN = preg_replace($regex, $replacement, $_SERVER['REMOTE_USER']); + + $CACHE[$this->index][$method] = $this->login($userDN, '', $method); + } + # Otherwise, use the user name as is + else { + $CACHE[$this->index][$method] = $this->login($_SERVER['REMOTE_USER'], '', $method); + } + + break; + + default: + $CACHE[$this->index][$method] = is_null($this->getLogin($method)) ? false : true; + } + + return $CACHE[$this->index][$method]; + } + + /** + * Logout of this datastore's connection method + */ + public function logout($method=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $method = $this->getMethod($method); + + unset ($_SESSION['cache'][$this->index]); + + switch ($this->getAuthType()) { + case 'cookie': + set_cookie($method.'-USER','',time()-3600,'/'); + set_cookie($method.'-PASS','',time()-3600,'/'); + + case 'config': + return true; + + case 'http': + case 'proxy': + case 'session': + case 'sasl': + if (isset($_SESSION['USER'][$this->index][$method])) + unset($_SESSION['USER'][$this->index][$method]); + + return true; + + default: + die(sprintf('Error: %s hasnt been configured for auth_type %s',__METHOD__,$this->getAuthType())); + } + } + + /** + * Functions that return the condition of the datasource + */ + public function isVisible() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->getValue('server','visible'); + } + + public function isReadOnly() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! trim($this->getLogin(null)) && $_SESSION[APPCONFIG]->getValue('appearance','anonymous_bind_implies_read_only')) + return true; + else + return $this->getValue('server','read_only'); + } + + public function getIndex() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->index); + + return $this->index; + } + + /** + * Work out which connection method to use. + * If a method is passed, then it will be passed back. If no method is passed, then we'll + * check to see if the user is logged in. If they are, then 'user' is used, otherwise + * 'anon' is used. + * + * @param int Server ID + * @return string Connection Method + */ + protected function getMethod($method=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + static $CACHE = array(); + + # Immediately return if method is set. + if (! is_null($method)) + return $method; + + # If we have been here already, then return our result + if (isset($CACHE[$this->index]) && ! is_null($CACHE)) + return $CACHE[$this->index]; + + $CACHE[$this->index] = 'anon'; + + if ($this->isLoggedIn('user')) + $CACHE[$this->index] = 'user'; + + return $CACHE[$this->index]; + } + + /** + * This method should be overridden in application specific ds files + */ + public function isSessionValid() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,1,__FILE__,__LINE__,__METHOD__,$fargs,true); + + return true; + } + + /** + * Return the time left in seconds until this connection times out. If there is not timeout, + * this function will return null. + */ + public function inactivityTime() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->isLoggedIn() && ! in_array($this->getAuthType(),array('config','http'))) + return time()+($this->getValue('login','timeout')*60); + else + return null; + } +} + +/** + * The list of database sources + * + * @package phpLDAPadmin + * @subpackage DataStore + */ +class Datastore { + # Out DS index id + private $index; + # List of all the objects + private $objects = array(); + # Default settings + private $default; + + public function __construct() { + $this->default = new StdClass; + + $this->default->server['id'] = array( + 'desc'=>'Server ID', + 'default'=>null); + + $this->default->server['name'] = array( + 'desc'=>'Server name', + 'default'=>null); + + # Connectivity Info + $this->default->server['host'] = array( + 'desc'=>'Host Name', + 'default'=>'127.0.0.1'); + + $this->default->server['port'] = array( + 'desc'=>'Port Number', + 'default'=>null); + + # Read or write only access + $this->default->server['read_only'] = array( + 'desc'=>'Server is in READ ONLY mode', + 'default'=>false); + + $this->default->server['visible'] = array( + 'desc'=>'Whether this server is visible', + 'default'=>true); + + # Authentication Information + $this->default->login['auth_type'] = array( + 'desc'=>'Authentication Type', + 'default'=>'session'); + +/* + /* ID to login to this application, this assumes that there is + * application authentication on top of authentication required to + * access the data source ** + $this->default->login['auth_id'] = array( + 'desc'=>'User Login ID to login to this DS', + 'untested'=>true, + 'default'=>null); + + $this->default->login['auth_pass'] = array( + 'desc'=>'User Login Password to login to this DS', + 'untested'=>true, + 'default'=>null); +*/ + + $this->default->login['auth_text'] = array( + 'desc'=>'Text to show at the login prompt', + 'default'=>null); + + $this->default->login['bind_id'] = array( + 'desc'=>'User Login ID to bind to this DS', + 'default'=>null); + + $this->default->login['bind_pass'] = array( + 'desc'=>'User Login Password to bind to this DS', + 'default'=>null); + + $this->default->login['timeout'] = array( + 'desc'=>'Session timout in seconds', + 'default'=>session_cache_expire()-1); + + $this->default->login['sasl_dn_regex'] = array( + 'desc'=>'SASL authorization id to user dn PCRE regular expression', + 'untested'=>true, + 'default'=>null); + + $this->default->login['sasl_dn_replacement'] = array( + 'desc'=>'SASL authorization id to user dn PCRE regular expression replacement string', + 'untested'=>true, + 'default'=>null); + + # Prefix for custom pages + $this->default->custom['pages_prefix'] = array( + 'desc'=>'Prefix name for custom pages', + 'default'=>'custom_'); + } + + /** + * Create a new database object + */ + public function newServer($type) { + if (class_exists($type)) { + $this->index = count($this->objects)+1; + $this->objects[$this->index] = new $type($this->index); + $this->objects[$this->index]->setDefaults($this->default); + return $this->index; + + } else { + printf('ERROR: Class [%s] doesnt exist',$type); + die(); + } + } + + /** + * Set values for a database object. + */ + public function setValue($key,$setting,$value) { + if (! $this->objects[$this->index]->isDefaultKey($key)) + error("ERROR: Setting a key [$key] that isnt predefined.",'error',true); + + if (! $this->objects[$this->index]->isDefaultSetting($key,$setting)) + error("ERROR: Setting a index [$key,$setting] that isnt predefined.",'error',true); + + # Test if its should be an array or not. + if (is_array($this->objects[$this->index]->getValue($key,$setting)) && ! is_array($value)) + error("Error in configuration file, {$key}['$setting'] SHOULD be an array of values.",'error',true); + + if (! is_array($this->objects[$this->index]->getValue($key,$setting)) && is_array($value)) + error("Error in configuration file, {$key}['$setting'] should NOT be an array of values.",'error',true); + + # Store the value in the object. + $this->objects[$this->index]->setValue($key,$setting,$value); + } + + /** + * Get a list of all the configured servers. + * + * @param boolean Only show visible servers. + * @return array list of all configured servers. + */ + public function getServerList($isVisible=true) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + static $CACHE; + + if (isset($CACHE[$isVisible])) + return $CACHE[$isVisible]; + + $CACHE[$isVisible] = array(); + + # Debugging incase objects is not set. + if (! $this->objects) { + print "
";
+			debug_print_backtrace();
+			die();
+		}
+
+		foreach ($this->objects as $id => $server)
+			if (! $isVisible || ($isVisible && $server->getValue('server','visible')))
+				$CACHE[$isVisible][$id] = $server;
+
+		masort($CACHE[$isVisible],'name');
+
+		return $CACHE[$isVisible];
+	}
+
+	/**
+	 * Return an object Instance of a configured database.
+	 *
+	 * @param int Index
+	 * @return object Datastore instance object.
+	 */
+	public function Instance($index=null) {
+		if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# If no index defined, then pick the lowest one.
+		if (is_null($index) || ! trim($index) || ! is_numeric($index))
+			$index = min($this->GetServerList())->getIndex();
+
+		if (! isset($this->objects[$index]))
+			debug_dump_backtrace(sprintf('Error: Datastore instance [%s] doesnt exist?',htmlspecialchars($index)),1);
+
+		if (defined('DEBUG_ENABLED') && DEBUG_ENABLED)
+			debug_log('Returning instance of database (%s)',3,0,__FILE__,__LINE__,__METHOD__,$index);
+
+		return $this->objects[$index];
+	}
+
+	/**
+	 * Return an object Instance of a configured database.
+	 *
+	 * @param string Name of the instance to retrieve
+	 * @return object Datastore instance object.
+	 */
+	public function InstanceName($name=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		foreach ($this->getServerList(false) as $index)
+			if ($this->objects[$index]->getName() == $name)
+				return $this->objects[$index];
+
+		# If we get here, then no object with the name exists.
+		return null;
+	}
+
+	/**
+	 * Return an object Instance of a configured database.
+	 *
+	 * @param string ID of the instance to retrieve
+	 * @return object Datastore instance object.
+	 */
+	public function InstanceId($id=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		foreach ($this->getServerList(false) as $index)
+			if ($this->objects[$index->getIndex()]->getValue('server','id') == $id)
+				return $this->objects[$index->getIndex()];
+
+		# If we get here, then no object with the name exists.
+		return null;
+	}
+}
+?>
diff --git a/lam/templates/3rdParty/pla/lib/ds_ldap_pla.php b/lam/templates/3rdParty/pla/lib/ds_ldap_pla.php
new file mode 100644
index 00000000..e84f56c8
--- /dev/null
+++ b/lam/templates/3rdParty/pla/lib/ds_ldap_pla.php
@@ -0,0 +1,653 @@
+default->appearance['password_hash'] = array(
+			'desc'=>'Default HASH to use for passwords',
+			'default'=>'md5');
+
+		$this->default->appearance['show_create'] = array(
+			'desc'=>'Whether to show the "Create new Entry here" in the tree browser',
+			'default'=>true);
+
+		$this->default->login['fallback_dn'] = array(
+			'desc'=>'If the attribute base login fails, see if a DN was entered',
+			'default'=>false);
+
+		$this->default->query['disable_default'] = array(
+			'desc'=>'Configuration to disable the default query template',
+			'default'=>false);
+
+		$this->default->query['custom_only'] = array(
+			'desc'=>'Configuration to force the usage of custom query templates',
+			'default'=>false);
+
+		$this->default->server['branch_rename'] = array(
+			'desc'=>'Enable renaming of branches',
+			'default'=>false);
+
+		$this->default->server['custom_attrs'] = array(
+			'desc'=>'Custom operational attributes to be treated as regular attributes',
+			'default'=>array(''));
+
+		$this->default->server['custom_sys_attrs'] = array(
+			'desc'=>'Custom operational attributes to be treated as internal attributes',
+			'default'=>array('+'));
+
+		$this->default->server['jpeg_attributes'] = array(
+			'desc'=>'Additional attributes to treat as Jpeg Attributes',
+			'default'=>array());
+
+		# This was added in case the LDAP server doesnt provide them with a base +,* query.
+		$this->default->server['root_dse_attributes'] = array(
+			'desc'=>'RootDSE attributes for use when displaying server info',
+			'default'=>array(
+				'namingContexts',
+				'subschemaSubentry',
+				'altServer',
+				'supportedExtension',
+				'supportedControl',
+				'supportedSASLMechanisms',
+				'supportedLDAPVersion',
+				'currentTime',
+				'dsServiceName',
+				'defaultNamingContext',
+				'schemaNamingContext',
+				'configurationNamingContext',
+				'rootDomainNamingContext',
+				'supportedLDAPPolicies',
+				'highestCommittedUSN',
+				'dnsHostName',
+				'ldapServiceName',
+				'serverName',
+				'supportedCapabilities',
+				'changeLog',
+				'tlsAvailableCipherSuites',
+				'tlsImplementationVersion',
+				'supportedSASLMechanisms',
+				'dsaVersion',
+				'myAccessPoint',
+				'dseType',
+				'+',
+				'*'
+			));
+
+		# Settings for auto_number
+		$this->default->auto_number['enable'] = array(
+			'desc'=>'Enable the AUTO UID feature',
+			'default'=>true);
+
+		$this->default->auto_number['mechanism'] = array(
+			'desc'=>'Mechanism to use to search for automatic numbers',
+			'default'=>'search');
+
+		$this->default->auto_number['search_base'] = array(
+			'desc'=>'Base DN to use for search mechanisms',
+			'default'=>null);
+
+		$this->default->auto_number['min'] = array(
+			'desc'=>'Minimum number to start with',
+			'default'=>array('uidNumber'=>1000,'gidNumber'=>500));
+
+		$this->default->auto_number['dn'] = array(
+			'desc'=>'DN to use when evaluating numbers',
+			'default'=>null);
+
+		$this->default->auto_number['pass'] = array(
+			'desc'=>'Password for DN to use when evaluating numbers',
+			'default'=>null);
+
+		$this->default->unique['attrs'] = array(
+			'desc'=>'Attributes to check for uniqueness before allowing updates',
+			'default'=>array('mail','uid','uidNumber'));
+
+		$this->default->unique['dn'] = array(
+			'desc'=>'DN to use when evaluating attribute uniqueness',
+			'default'=>null);
+
+		$this->default->unique['pass'] = array(
+			'desc'=>'Password for DN to use when evaluating attribute uniqueness',
+			'default'=>null);
+	}
+
+	public function __get($key) {
+		switch ($key) {
+			case 'name':
+				return $this->getValue('server','name');
+
+			default:
+				system_message(array(
+					'title'=>_('Unknown request for Object value.'),
+					'body'=>sprintf(_('Attempt to obtain value %s from %s'),$key,get_class($this)),
+					'type'=>'error'));
+		}
+	}
+
+	/**
+	 * Gets whether the admin has configured phpLDAPadmin to show the "Create New" link in the tree viewer.
+	 * 
+	 *	$servers->setValue('appearance','show_create',true|false);
+	 * 
+	 * If NOT set, then default to show the Create New item.
+	 * If IS set, then return the value (it should be true or false).
+	 *
+	 * The entry creation command must be available.
+	 * 
+	 *	$config->custom->commands['script'] = array('create' => true);
+	 * 
+	 *
+	 * @return boolean true if the feature is enabled and false otherwise.
+	 */
+	function isShowCreateEnabled() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		if (! $_SESSION[APPCONFIG]->isCommandAvailable('script','create'))
+			return false;
+		else
+			return $this->getValue('appearance','show_create');
+	}
+
+	/**
+	 * Fetch whether the user has configured a certain server login to be non anonymous
+	 *
+	 * 
+	 *	$servers->setValue('login','anon_bind',true|false);
+	 * 
+	 *
+	 * @return boolean
+	 */
+	public function isAnonBindAllowed() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# If only_login_allowed_dns is set, then we cant have anonymous.
+		if (count($this->getValue('login','allowed_dns')) > 0)
+			$return = false;
+		else
+			$return = $this->getValue('login','anon_bind');
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
+
+		return $return;
+	}
+
+	/**
+	 * Returns true if the user has configured the specified server to enable branch (non-leaf) renames.
+	 *
+	 * This is configured in config.php thus:
+	 * 
+	 *	$servers->setValue('server','branch_rename',true|false);
+	 * 
+	 *
+	 * @return boolean
+	 */
+	function isBranchRenameEnabled() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+ 		return $this->getValue('server','branch_rename');
+	}
+
+	/**
+	 * Determines if an attribute's value can contain multiple lines. Attributes that fall
+	 * in this multi-line category may be configured in config.php. Hence, this function
+	 * accesses the global variable $_SESSION[APPCONFIG]->custom->appearance['multi_line_attributes'];
+	 *
+	 * Usage example:
+	 * 
+	 *	if ($ldapserver->isMultiLineAttr('postalAddress'))
+	 *		echo '';
+	 *	else
+	 *		echo '';
+	 * 
+	 *
+	 * @param string The name of the attribute of interested (case insensivite)
+	 * @param string (optional) The current value of the attribute (speeds up the process by searching for carriage returns already in the attribute value)
+	 * @return boolean
+	 */
+	function isMultiLineAttr($attr_name,$val=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# Set default return
+		$return = false;
+
+		# First, check the optional val param for a \n or a \r
+		if (! is_null($val) && (strpos($val,"\n") || strpos($val,"\r")))
+			$return = true;
+
+		# Next, compare strictly by name first
+		else
+			foreach ($_SESSION[APPCONFIG]->getValue('appearance','multi_line_attributes') as $multi_line_attr_name)
+				if (strcasecmp($multi_line_attr_name,$attr_name) == 0) {
+					$return = true;
+					break;
+				}
+
+		# If unfound, compare by syntax OID
+		if (! $return) {
+			$sattr = $this->getSchemaAttribute($attr_name);
+
+			if ($sattr) {
+				$syntax_oid = $sattr->getSyntaxOID();
+
+				if ($syntax_oid)
+					foreach ($_SESSION[APPCONFIG]->getValue('appearance','multi_line_syntax_oids') as $multi_line_syntax_oid)
+						if ($multi_line_syntax_oid == $syntax_oid) {
+							$return = true;
+							break;
+						}
+			}
+		}
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
+
+		return $return;
+	}
+
+	/**
+	 * Returns true if the specified attribute is configured according to
+	 * the test enabled in config.php
+	 *
+	 * @param string The name of the attribute to test.
+	 * @param array The attributes to test against.
+	 * @param dn A DN that is exempt from these tests.
+	 * @return boolean
+	 */
+	private function isAttrTest($attr,$attrs,$except_dn) {
+		$attr = trim($attr);
+		if (! trim($attr) || ! count($attrs))
+			return false;
+
+		# Is the user excluded?
+		if ($except_dn && $this->userIsMember($this->getLogin(),$except_dn))
+			return false;
+
+		foreach ($attrs as $attr_name)
+			if (strcasecmp($attr,trim($attr_name)) == 0)
+				return true;
+
+		return false;
+	}
+
+	/**
+	 * Returns true if the specified attribute is configured as read only
+	 * in config.php.
+	 * Attributes are configured as read-only in config.php thus:
+	 * 
+	 *	$config->custom->appearance['readonly_attrs'] = array('objectClass');
+	 * 
+	 *
+	 * @param string The name of the attribute to test.
+	 * @return boolean
+	 */
+	public function isAttrReadOnly($attr) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$attrs = $_SESSION[APPCONFIG]->getValue('appearance','readonly_attrs');
+		$except_dn = $_SESSION[APPCONFIG]->getValue('appearance','readonly_attrs_exempt');
+
+		return $this->isAttrTest($attr,$attrs,$except_dn);
+	}
+
+	/**
+	 * Returns true if the specified attribute is configured as hidden
+	 * in config.php.
+	 * Attributes are configured as hidden in config.php thus:
+	 * 
+	 *	$config->custom->appearance['hide_attrs'] = array('objectClass');
+	 * 
+	 *
+	 * @param string The name of the attribute to test.
+	 * @return boolean
+	 */
+	public function isAttrHidden($attr) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$attrs = $_SESSION[APPCONFIG]->getValue('appearance','hide_attrs');
+		$except_dn = $_SESSION[APPCONFIG]->getValue('appearance','hide_attrs_exempt');
+
+		return $this->isAttrTest($attr,$attrs,$except_dn);
+	}
+
+	/**
+	 * Add objects
+	 */
+	public function add($dn,$entry_array,$method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		foreach ($entry_array as $attr => $val)
+			$entry_array[$attr] = dn_unescape($val);
+
+		$result = false;
+
+		# Check our unique attributes.
+		if (! $this->checkUniqueAttrs($dn,$entry_array))
+			return false;
+
+		if (run_hook('pre_entry_create',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attrs'=>$entry_array))) {
+			$result = @ldap_add($this->connect($method),dn_escape($dn),$entry_array);
+
+			if ($result) {
+				# Update the tree
+				$tree = get_cached_item($this->index,'tree');
+
+				# If we created the base, delete it, then add it back
+				if (get_request('create_base'))
+					$tree->delEntry($dn);
+
+				$tree->addEntry($dn);
+
+				set_cached_item($this->index,'tree','null',$tree);
+
+				run_hook('post_entry_create',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attrs'=>$entry_array));
+
+			} else {
+				system_message(array(
+					'title'=>_('Could not add the object to the LDAP server.'),
+					'body'=>ldap_error_msg($this->getErrorMessage(null),$this->getErrorNum(null)),
+					'type'=>'error'));
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Delete objects
+	 */
+	public function delete($dn,$method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$result = false;
+
+		if (run_hook('pre_entry_delete',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn))) {
+			$result = @ldap_delete($this->connect($method),dn_escape($dn));
+
+			if ($result) {
+				# Update the tree
+				$tree = get_cached_item($this->index,'tree');
+				$tree->delEntry($dn);
+
+				set_cached_item($this->index,'tree','null',$tree);
+
+				run_hook('post_entry_delete',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn));
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Rename objects
+	 */
+	public function rename($dn,$new_rdn,$container,$deleteoldrdn,$method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$result = false;
+
+		if (run_hook('pre_entry_rename',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'rdn'=>$new_rdn,'container'=>$container))) {
+			$result = @ldap_rename($this->connect($method),$dn,$new_rdn,$container,$deleteoldrdn);
+
+			if ($result) {
+				# Update the tree
+				$tree = get_cached_item($this->index,'tree');
+				$newdn = sprintf('%s,%s',$new_rdn,$container);
+				$tree->renameEntry($dn,$newdn);
+
+				set_cached_item($this->index,'tree','null',$tree);
+
+				run_hook('post_entry_rename',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'rdn'=>$new_rdn,'container'=>$container));
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Modify objects
+	 */
+	public function modify($dn,$attrs,$method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# Check our unique attributes.
+		if (! $this->checkUniqueAttrs($dn,$attrs))
+			return false;
+
+		$result = false;
+		$summary = array();
+		$current_attrs = $this->getDNAttrValues($dn,$method,LDAP_DEREF_NEVER,array('*'));
+
+		# Go through our attributes and call our hooks for each attribute changing its value
+		foreach ($attrs as $attr => $values) {
+			# For new attributes
+			if (count($values) && ! isset($current_attrs[$attr])) {
+				if (! run_hook('pre_attr_add',
+					array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attr'=>$attr,'newvalue'=>$values))) {
+
+					unset($attrs[$attr]);
+					system_message(array(
+						'title'=>_('Attribute not added'),
+						'body'=>sprintf('%s (%s)',_('Hook pre_attr_add prevented attribute from being added'),$attr),
+						'type'=>'warn'));
+
+				} else
+					$summary['add'][$attr]['new'] = $values;
+
+			# For modify attributes
+			} elseif (count($values)) {
+				if (! run_hook('pre_attr_modify',
+					array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attr'=>$attr,'oldvalue'=>$current_attrs[$attr],'newvalue'=>$values))) {
+
+					unset($attrs[$attr]);
+					system_message(array(
+						'title'=>_('Attribute not modified'),
+						'body'=>sprintf('%s (%s)',_('Hook pre_attr_modify prevented attribute from being modified'),$attr),
+						'type'=>'warn'));
+
+				} else {
+					$summary['modify'][$attr]['new'] = $values;
+					$summary['modify'][$attr]['old'] = $current_attrs[$attr];
+				}
+
+			# For delete attributes
+			} else {
+				if (! run_hook('pre_attr_delete',
+					array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attr'=>$attr,'oldvalue'=>$current_attrs[$attr]))) {
+
+					unset($attrs[$attr]);
+					system_message(array(
+						'title'=>_('Attribute not deleted'),
+						'body'=>sprintf('%s (%s)',_('Hook pre_attr_delete prevented attribute from being deleted'),$attr),
+						'type'=>'warn'));
+
+				} else
+					$summary['delete'][$attr]['old'] = $current_attrs[$attr];
+			}
+		}
+
+		if (! count($attrs))
+			return false;
+
+		if (run_hook('pre_entry_modify',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attrs'=>$attrs))) {
+			$result = @ldap_modify($this->connect($method),$dn,$attrs);
+
+			if ($result) {
+				run_hook('post_entry_modify',array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attrs'=>$attrs));
+
+				foreach (array('add','modify','delete') as $mode)
+					if (isset($summary[$mode]))
+						foreach ($summary[$mode] as $attr => $values)
+							switch ($mode) {
+								case 'add':
+									run_hook(sprintf('post_attr_%s',$mode),
+										array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attr'=>$attr,'newvalue'=>$values['new']));
+									break;
+
+								case 'modify':
+									run_hook(sprintf('post_attr_%s',$mode),
+										array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attr'=>$attr,'oldvalue'=>$values['old'],'newvalue'=>$values['new']));
+									break;
+
+								case 'delete':
+									run_hook(sprintf('post_attr_%s',$mode),
+										array('server_id'=>$this->index,'method'=>$method,'dn'=>$dn,'attr'=>$attr,'oldvalue'=>$values['old']));
+									break;
+
+								default:
+									debug_dump_backtrace(sprintf('Unkown mode %s',$mode),1);
+							}
+			} else {
+				system_message(array(
+					'title'=>_('Could not perform ldap_modify operation.'),
+					'body'=>ldap_error_msg($this->getErrorMessage($method),$this->getErrorNum($method)),
+					'type'=>'error'));
+			}
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Returns true if the specified attribute is configured as unique
+	 * in config.php.
+	 * Attributes are configured as hidden in config.php thus:
+	 * 
+	 *	$servers->setValue('unique','attrs',array('mail','uid','uidNumber'));
+	 * 
+	 *
+	 * @param string $attr The name of the attribute to test.
+	 * @return boolean
+	 */
+	public function isAttrUnique($attr) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# Should this attribute value be unique
+		if (in_array_ignore_case($attr,$this->getValue('unique','attrs')))
+			return true;
+		else
+			return false;
+	}
+
+	/**
+	 * This function will check whether the value for an attribute being changed
+	 * is already assigned to another DN.
+	 *
+	 * Returns the bad value, or null if all values are OK
+	 *
+	 * @param dn DN that is being changed
+	 * @param string Attribute being changed
+	 * @param string|array New values for the attribute
+	 */
+	public function checkUniqueAttrs($dn,$attrs) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# If none of the attributes are defined unique, we'll return immediately;
+		if (! $checkattrs = array_intersect(arrayLower($this->getValue('unique','attrs')),array_keys(array_change_key_case($attrs))))
+			return true;
+
+		# Check see and use our alternate uid_dn and password if we have it.
+		if (! $this->login($this->getValue('unique','dn'),$this->getValue('unique','pass'),'unique')) {
+			system_message(array(
+				'title'=>_('UNIQUE invalid login/password'),
+				'body'=>sprintf('%s (%s)',_('Unable to connect to LDAP server with the unique login/password, please check your configuration.'),
+					$this->getName()),
+				'type'=>'warn'));
+
+			return false;
+		}
+
+		$query = array();
+
+		# Build our search filter to double check each attribute.
+		$query['filter'] = '(|';
+		foreach ($checkattrs as $attr)
+			foreach ($attrs[$attr] as $val)
+				if ($val)
+					$query['filter'] .= sprintf('(%s=%s)',$attr,$val);
+		$query['filter'] .= ')';
+
+		$query['attrs'] = $checkattrs;
+
+		# Search through our bases and see if we have match
+		foreach ($this->getBaseDN() as $base) {
+			$query['base'] = $base;
+
+			# Do the search
+			$results = $this->query($query,'unique');
+
+			# If we have a match.
+			if (count($results))
+				foreach ($results as $values)
+					# If one of the attributes is owned to somebody else, then we may as well die here.
+					if ($values['dn'] != $dn) {
+						$href = sprintf('cmd.php?cmd=query_engine&server_id=%s&filter=%s&scope=sub&query=none&format=list&search=true',$this->index,$query['filter']);
+
+						system_message(array(
+							'title'=>_('Attribute value would not be unique'),
+							'body'=>sprintf('%s (%s)',
+								_('This update has been or will be cancelled, it would result in an attribute value not being unique. You might like to search the LDAP server for the offending entry.'),
+								htmlspecialchars($href),
+								_('Search')),
+							'type'=>'warn'));
+
+						return false;
+					}
+		}
+
+		# If we get here, then it must be OK?
+		return true;
+	}
+
+	/**
+	 * Check if the session timeout has occured for this LDAP server.
+	 */
+	public function isSessionValid() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# If inactiveTime() returns a value, we need to check that it has not expired.
+		if (is_null($this->inactivityTime()) || ! $this->isLoggedIn())
+			return true;
+
+		# If session has expired
+		if ((isset($_SESSION['ACTIVITY'][$this->getIndex()])) && ($_SESSION['ACTIVITY'][$this->getIndex()] < time())) {
+			$this->logout();
+			unset($_SESSION['ACTIVITY'][$this->getIndex()]);
+
+			return false;
+		}
+
+		$_SESSION['ACTIVITY'][$this->getIndex()] = $this->inactivityTime();
+		return true;
+	}
+}
+?>
diff --git a/lam/templates/3rdParty/pla/lib/ds_myldap.php b/lam/templates/3rdParty/pla/lib/ds_myldap.php
new file mode 100644
index 00000000..48b733a0
--- /dev/null
+++ b/lam/templates/3rdParty/pla/lib/ds_myldap.php
@@ -0,0 +1,2334 @@
+index = $index;
+		$this->type = 'ldap';
+
+		# Additional values that can go in our config.php
+		$this->custom = new StdClass;
+		$this->default = new StdClass;
+
+/*
+ * Not used by PLA
+		# Database Server Variables
+		$this->default->server['db'] = array(
+			'desc'=>'Database Name',
+			'untested'=>true,
+			'default'=>null);
+*/
+
+		/* This was created for IDS - since it doesnt present STRUCTURAL against objectClasses
+		 * definitions when reading the schema.*/
+		$this->default->server['schema_oclass_default'] = array(
+			'desc'=>'When reading the schema, and it doesnt specify objectClass type, default it to this',
+			'default'=>null);
+
+		$this->default->server['base'] = array(
+			'desc'=>'LDAP Base DNs',
+			'default'=>array());
+
+		$this->default->server['tls'] = array(
+			'desc'=>'Connect using TLS',
+			'default'=>false);
+
+		# Login Details
+		$this->default->login['attr'] = array(
+			'desc'=>'Attribute to use to find the users DN',
+			'default'=>'dn');
+
+		$this->default->login['anon_bind'] = array(
+			'desc'=>'Enable anonymous bind logins',
+			'default'=>true);
+
+		$this->default->login['allowed_dns'] = array(
+			'desc'=>'Limit logins to users who match any of the following LDAP filters',
+			'default'=>array());
+
+		$this->default->login['base'] = array(
+			'desc'=>'Limit logins to users who are in these base DNs',
+			'default'=>array());
+
+		$this->default->login['class'] = array(
+			'desc'=>'Strict login to users containing a specific objectClasses',
+			'default'=>array());
+
+		$this->default->proxy['attr'] = array(
+			'desc'=>'Attribute to use to find the users DN for proxy based authentication',
+			'default'=>array());
+
+		# SASL configuration
+		$this->default->server['sasl'] = array(
+			'desc'=>'Use SASL authentication when binding LDAP server',
+			'default'=>false);
+
+		$this->default->sasl['mech'] = array(
+			'desc'=>'SASL mechanism used while binding LDAP server',
+			'untested'=>true,
+			'default'=>'PLAIN');
+
+		$this->default->sasl['realm'] = array(
+			'desc'=>'SASL realm name',
+			'untested'=>true,
+			'default'=>null);
+
+		$this->default->sasl['authz_id'] = array(
+			'desc'=>'SASL authorization id',
+			'untested'=>true,
+			'default'=>null);
+
+		$this->default->sasl['authz_id_regex'] = array(
+			'desc'=>'SASL authorization id PCRE regular expression',
+			'untested'=>true,
+			'default'=>null);
+
+		$this->default->sasl['authz_id_replacement'] = array(
+			'desc'=>'SASL authorization id PCRE regular expression replacement string',
+			'untested'=>true,
+			'default'=>null);
+
+		$this->default->sasl['props'] = array(
+			'desc'=>'SASL properties',
+			'untested'=>true,
+			'default'=>null);
+	}
+
+	/**
+	 * Required ABSTRACT functions
+	 */
+	/**
+	 * Connect and Bind to the Database
+	 *
+	 * @param string Which connection method resource to use
+	 * @return resource|null Connection resource if successful, null if not.
+	 */
+	protected function connect($method,$debug=false,$new=false) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		static $CACHE = array();
+
+		$method = $this->getMethod($method);
+		$bind = array();
+
+		if (isset($CACHE[$this->index][$method]) && $CACHE[$this->index][$method])
+			return $CACHE[$this->index][$method];
+
+		# Check if we have logged in and therefore need to use those details as our bind.
+		$bind['id'] = is_null($this->getLogin($method)) && $method != 'anon' ? $this->getLogin('user') : $this->getLogin($method);
+		$bind['pass'] = is_null($this->getPassword($method)) && $method != 'anon' ? $this->getPassword('user') : $this->getPassword($method);
+
+		# If our bind id is still null, we are not logged in.
+		if (is_null($bind['id']) && ! in_array($method,array('anon','login')))
+			return null;
+
+		# If we bound to the LDAP server with these details for a different connection, return that resource
+		if (isset($CACHE[$this->index]) && ! $new)
+			foreach ($CACHE[$this->index] as $cachedmethod => $resource) {
+				if (($this->getLogin($cachedmethod) == $bind['id']) && ($this->getPassword($cachedmethod) == $bind['pass'])) {
+					$CACHE[$this->index][$method] = $resource;
+
+					return $CACHE[$this->index][$method];
+				}
+			}
+
+		$CACHE[$this->index][$method] = null;
+
+		# No identifiable connection exists, lets create a new one.
+		if (DEBUG_ENABLED)
+			debug_log('Creating NEW connection [%s] for index [%s]',16,0,__FILE__,__LINE__,__METHOD__,
+				$method,$this->index);
+
+		if (function_exists('run_hook'))
+			run_hook('pre_connect',array('server_id'=>$this->index,'method'=>$method));
+
+		if ($this->getValue('server','port'))
+			$resource = ldap_connect($this->getValue('server','host'),$this->getValue('server','port'));
+		else
+			$resource = ldap_connect($this->getValue('server','host'));
+
+		$CACHE[$this->index][$method] = $resource;
+
+		if (DEBUG_ENABLED)
+			debug_log('LDAP Resource [%s], Host [%s], Port [%s]',16,0,__FILE__,__LINE__,__METHOD__,
+				$resource,$this->getValue('server','host'),$this->getValue('server','port'));
+
+		if (! is_resource($resource))
+			debug_dump_backtrace('UNHANDLED, $resource is not a resource',1);
+
+		# Go with LDAP version 3 if possible (needed for renaming and Novell schema fetching)
+		ldap_set_option($resource,LDAP_OPT_PROTOCOL_VERSION,3);
+
+		/* Disabling this makes it possible to browse the tree for Active Directory, and seems
+		 * to not affect other LDAP servers (tested with OpenLDAP) as phpLDAPadmin explicitly
+		 * specifies deref behavior for each ldap_search operation. */
+		ldap_set_option($resource,LDAP_OPT_REFERRALS,0);
+
+		# Try to fire up TLS is specified in the config
+		if ($this->isTLSEnabled())
+			$this->startTLS($resource);
+
+		# If SASL has been configured for binding, then start it now.
+		if ($this->isSASLEnabled())
+			$bind['result'] = $this->startSASL($resource,$method);
+
+		# Normal bind...
+		else
+			$bind['result'] = @ldap_bind($resource,$bind['id'],$bind['pass']);
+
+		if ($debug)
+			debug_dump(array('method'=>$method,'bind'=>$bind,'USER'=>$_SESSION['USER']));
+
+		if (DEBUG_ENABLED)
+			debug_log('Resource [%s], Bind Result [%s]',16,0,__FILE__,__LINE__,__METHOD__,$resource,$bind);
+
+		if (! $bind['result']) {
+			if (DEBUG_ENABLED)
+				debug_log('Leaving with FALSE, bind FAILed',16,0,__FILE__,__LINE__,__METHOD__);
+
+			$this->noconnect = true;
+
+			system_message(array(
+				'title'=>sprintf('%s %s',_('Unable to connect to LDAP server'),$this->getName()),
+				'body'=>sprintf('%s: %s (%s) for %s',_('Error'),$this->getErrorMessage($method),$this->getErrorNum($method),$method),
+				'type'=>'error'));
+
+			$CACHE[$this->index][$method] = null;
+
+		} else {
+			$this->noconnect = false;
+
+			# If this is a proxy session, we need to switch to the proxy user
+			if ($this->isProxyEnabled() && $bind['id'] && $method != 'anon')
+				if (! $this->startProxy($resource,$method)) {
+					$this->noconnect = true;
+					$CACHE[$this->index][$method] = null;
+				}
+		}
+
+		if (function_exists('run_hook'))
+			run_hook('post_connect',array('server_id'=>$this->index,'method'=>$method,'id'=>$bind['id']));
+
+		if ($debug)
+			debug_dump(array($method=>$CACHE[$this->index][$method]));
+
+		return $CACHE[$this->index][$method];
+	}
+
+	/**
+	 * Login to the database with the application user/password
+	 *
+	 * @return boolean true|false for successful login.
+	 */
+	public function login($user=null,$pass=null,$method=null,$new=false) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$userDN = null;
+
+		# Get the userDN from the username.
+		if (! is_null($user)) {
+			# If login,attr is set to DN, then user should be a DN
+			if (($this->getValue('login','attr') == 'dn') || $method != 'user')
+				$userDN = $user;
+			else
+				$userDN = $this->getLoginID($user,'login');
+
+			if (! $userDN && $this->getValue('login','fallback_dn'))
+				$userDN = $user;
+
+			if (! $userDN)
+				return false;
+
+		} else {
+			if (in_array($method,array('user','anon'))) {
+				$method = 'anon';
+				$userDN = '';
+				$pass = '';
+
+			} else {
+				$userDN = $this->getLogin('user');
+				$pass = $this->getPassword('user');
+			}
+		}
+
+		if (! $this->isAnonBindAllowed() && ! trim($userDN))
+			return false;
+
+		# Temporarily set our user details
+		$this->setLogin($userDN,$pass,$method);
+
+		$connect = $this->connect($method,false,$new);
+
+		# If we didnt log in...
+		if (! is_resource($connect) || $this->noconnect || ! $this->userIsAllowedLogin($userDN)) {
+			$this->logout($method);
+
+			return false;
+
+		} else
+			return true;
+	}
+
+	/**
+	 * Perform a query to the Database
+	 *
+	 * @param string query to perform
+	 *	$query['base']
+	 *	$query['filter']
+	 *	$query['scope']
+	 *	$query['attrs'] = array();
+	 *	$query['deref']
+	 * @param string Which connection method resource to use
+	 * @param string Index items according to this key
+	 * @param boolean Enable debugging output
+	 * @return array|null Results of query.
+	 */
+	public function query($query,$method,$index=null,$debug=false) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$attrs_only = 0;
+
+		# Defaults
+		if (! isset($query['attrs']))
+			$query['attrs'] = array();
+		else
+			# Re-index the attrs, PHP throws an error if the keys are not sequential from 0.
+			$query['attrs'] = array_values($query['attrs']);
+
+		if (! isset($query['base'])) {
+			$bases = $this->getBaseDN();
+			$query['base'] = array_shift($bases);
+		}
+
+		if (! isset($query['deref']))
+			$query['deref'] = $_SESSION[APPCONFIG]->getValue('deref','search');
+		if (! isset($query['filter']))
+			$query['filter'] = '(&(objectClass=*))';
+		if (! isset($query['scope']))
+			$query['scope'] = 'sub';
+		if (! isset($query['size_limit']))
+			$query['size_limit'] = 0;
+		if (! isset($query['time_limit']))
+			$query['time_limit'] = 0;
+
+		if ($query['scope'] == 'base' && ! isset($query['baseok']))
+			system_message(array(
+				'title'=>sprintf('Dont call %s',__METHOD__),
+				'body'=>sprintf('Use getDNAttrValues for base queries [%s]',$query['base']),
+				'type'=>'info'));
+
+		if (is_array($query['base'])) {
+			system_message(array(
+				'title'=>_('Invalid BASE for query'),
+				'body'=>_('The query was cancelled because of an invalid base.'),
+				'type'=>'error'));
+
+			return array();
+		}
+
+		if (DEBUG_ENABLED)
+			debug_log('%s search PREPARE.',16,0,__FILE__,__LINE__,__METHOD__,$query['scope']);
+
+		if ($debug)
+			debug_dump(array('query'=>$query,'server'=>$this->getIndex(),'con'=>$this->connect($method)));
+
+		$resource = $this->connect($method,$debug);
+
+		switch ($query['scope']) {
+			case 'base':
+				$search = @ldap_read($resource,$query['base'],$query['filter'],$query['attrs'],$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
+				break;
+
+			case 'one':
+				$search = @ldap_list($resource,$query['base'],$query['filter'],$query['attrs'],$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
+				break;
+
+			case 'sub':
+			default:
+				$search = @ldap_search($resource,$query['base'],$query['filter'],$query['attrs'],$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
+				break;
+		}
+
+		if ($debug)
+			debug_dump(array('method'=>$method,'search'=>$search,'error'=>$this->getErrorMessage()));
+
+		if (DEBUG_ENABLED)
+			debug_log('Search scope [%s] base [%s] filter [%s] attrs [%s] COMPLETE (%s).',16,0,__FILE__,__LINE__,__METHOD__,
+				$query['scope'],$query['base'],$query['filter'],$query['attrs'],is_null($search));
+
+		if (! $search)
+			return array();
+
+		$return = array();
+
+		# Get the first entry identifier
+		if ($entries = ldap_get_entries($resource,$search)) {
+			# Remove the count
+			if (isset($entries['count']))
+				unset($entries['count']);
+
+			# Iterate over the entries
+			foreach ($entries as $a => $entry) {
+				if (! isset($entry['dn']))
+					debug_dump_backtrace('No DN?',1);
+
+				# Remove the none entry references.
+				if (! is_array($entry)) {
+					unset($entries[$a]);
+					continue;
+				}
+
+				$dn = $entry['dn'];
+				unset($entry['dn']);
+
+				# Iterate over the attributes
+				foreach ($entry as $b => $attrs) {
+					# Remove the none entry references.
+					if (! is_array($attrs)) {
+						unset($entry[$b]);
+						continue;
+					}
+
+					# Remove the count
+					if (isset($entry[$b]['count']))
+						unset($entry[$b]['count']);
+				}
+
+				# Our queries always include the DN (the only value not an array).
+				$entry['dn'] = $dn;
+				$return[$dn] = $entry;
+			}
+
+			# Sort our results
+			foreach ($return as $key=> $values)
+				ksort($return[$key]);
+		}
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
+
+		return $return;
+	}
+
+	/**
+	 * Get the last error string
+	 *
+	 * @param string Which connection method resource to use
+	 */
+	public function getErrorMessage($method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		return ldap_error($this->connect($method));
+	}
+
+	/**
+	 * Get the last error number
+	 *
+	 * @param string Which connection method resource to use
+	 */
+	public function getErrorNum($method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		return ldap_errno($this->connect($method));
+	}
+
+	/**
+	 * Additional functions
+	 */
+	/**
+	 * Get a user ID
+	 *
+	 * @param string Which connection method resource to use
+	 */
+	public function getLoginID($user,$method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$query['filter'] = sprintf('(&(%s=%s)%s)',
+			$this->getValue('login','attr'),$user,
+			$this->getLoginClass() ? sprintf('(objectclass=%s)',join(')(objectclass=',$this->getLoginClass())) : '');
+		$query['attrs'] = array('dn');
+
+		$result = array();
+		foreach ($this->getLoginBaseDN() as $base) {
+			$query['base'] = $base;
+			$result = $this->query($query,$method);
+
+			if (count($result) == 1)
+				break;
+		}
+
+		if (count($result) != 1)
+			return null;
+
+		$detail = array_shift($result);
+
+		if (! isset($detail['dn']))
+			die('ERROR: DN missing?');
+		else
+			return $detail['dn'];
+	}
+
+	/**
+	 * Return the login base DNs
+	 * If no login base DNs are defined, then the LDAP server Base DNs are used.
+	 */
+	private function getLoginBaseDN() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,1,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		if ($this->getValue('login','base'))
+			return $this->getValue('login','base');
+		else
+			return $this->getBaseDN();
+	}
+
+	/**
+	 * Return the login classes that a user must have to login
+	 */
+	private function getLoginClass() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,1,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		return $this->getValue('login','class');
+	}
+
+	/**
+	 * Return if anonymous bind is allowed in the configuration
+	 */
+	public function isAnonBindAllowed() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		return $this->getValue('login','anon_bind');
+	}
+
+	/**
+	 * Fetches whether TLS has been configured for use with a certain server.
+	 *
+	 * Users may configure phpLDAPadmin to use TLS in config,php thus:
+	 * 
+	 *	$servers->setValue('server','tls',true|false);
+	 * 
+	 *
+	 * @return boolean
+	 */
+	private function isTLSEnabled() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		if ($this->getValue('server','tls') && ! function_exists('ldap_start_tls')) {
+				error(_('TLS has been enabled in your config, but your PHP install does not support TLS. TLS will be disabled.'),'warn');
+			return false;
+
+		} else
+			return $this->getValue('server','tls');
+	}
+
+	/**
+	 * If TLS is configured, then start it
+	 */
+	private function startTLS($resource) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		if (! $this->getValue('server','tls') || (function_exists('ldap_start_tls') && ! @ldap_start_tls($resource))) {
+			system_message(array(
+				'title'=>sprintf('%s (%s)',_('Could not start TLS.'),$this->getName()),
+				'body'=>sprintf('%s: %s',_('Error'),_('Could not start TLS. Please check your LDAP server configuration.')),
+				'type'=>'error'));
+
+			return false;
+
+		} else
+			return true;
+	}
+
+	/**
+	 * Fetches whether SASL has been configured for use with a certain server.
+	 *
+	 * Users may configure phpLDAPadmin to use SASL in config,php thus:
+	 * 
+	 *	$servers->setValue('server','sasl',true|false);
+	 * 
+	 *
+	 * @return boolean
+	 */
+	private function isSASLEnabled() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		if ($this->getValue('server','sasl') && ! function_exists('ldap_sasl_bind')) {
+				error(_('SASL has been enabled in your config, but your PHP install does not support SASL. SASL will be disabled.'),'warn');
+			return false;
+
+		} else
+			return $this->getValue('server','sasl');
+	}
+
+	/**
+	 * If SASL is configured, then start it
+	 * To be able to use SASL, PHP should have been compliled with --with-ldap-sasl=DIR
+	 *
+	 * @todo This has not been tested, please let the developers know if this function works as expected.
+	 */
+	private function startSASL($resource,$method) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		static $CACHE = array();
+
+		if (! $this->getValue('server','sasl') || ! function_exists('ldap_start_tls'))
+			return false;
+
+		if (! isset($CACHE['login_dn'])) {
+			$CACHE['login_dn'] = is_null($this->getLogin($method)) ? $this->getLogin('user') : $this->getLogin($method);
+			$CACHE['login_pass'] = is_null($this->getPassword($method)) ? $this->getPassword('user') : $this->getPassword($method);
+		}
+
+		$mech = strtolower($this->getValue('sasl','mech'));
+
+		# Do we need to rewrite authz_id?
+		if (! isset($CACHE['authz_id']))
+			if (! trim($this->getValue('sasl','authz_id')) && $mech != 'gssapi') {
+
+			if (DEBUG_ENABLED)
+				debug_log('Rewriting bind DN [%s] -> authz_id with regex [%s] and replacement [%s].',9,0,__FILE__,__LINE__,__METHOD__,
+					$CACHE['login_dn'],
+					$this->getValue('sasl','authz_id_regex'),
+					$this->getValue('sasl','authz_id_replacement'));
+
+			$CACHE['authz_id'] = @preg_replace($this->getValue('sasl','authz_id_regex'),
+				$this->getValue('sasl','authz_id_replacement'),$CACHE['login_dn']);
+
+			# Invalid regex?
+			if (is_null($CACHE['authz_id']))
+				error(sprintf(_('It seems that sasl_authz_id_regex "%s" contains invalid PCRE regular expression. The error is "%s".'),
+					$this->getValue('sasl','authz_id_regex'),(isset($php_errormsg) ? $php_errormsg : '')),
+					'error','index.php');
+
+			if (DEBUG_ENABLED)
+				debug_log('Resource [%s], SASL OPTIONS: mech [%s], realm [%s], authz_id [%s], props [%s]',9,0,__FILE__,__LINE__,__METHOD__,
+					$resource,
+					$this->getValue('sasl','mech'),
+					$this->getValue('sasl','realm'),
+					$CACHE['authz_id'],
+					$this->getValue('sasl','props'));
+
+			} else
+				$CACHE['authz_id'] = $this->getValue('sasl','authz_id');
+
+		# @todo this function is different in PHP5.1 and PHP5.2
+		return @ldap_sasl_bind($resource,$CACHE['login_dn'],$CACHE['login_pass'],
+			$this->getValue('sasl','mech'),
+			$this->getValue('sasl','realm'),
+			$CACHE['authz_id'],
+			$this->getValue('sasl','props'));
+	}
+
+	/**
+	 * Fetches whether PROXY AUTH has been configured for use with a certain server.
+	 *
+	 * Users may configure phpLDAPadmin to use PROXY AUTH in config,php thus:
+	 * 
+	 *	$servers->setValue('login','auth_type','proxy');
+	 * 
+	 *
+	 * @return boolean
+	 */
+	private function isProxyEnabled() {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		return $this->getValue('login','auth_type') == 'proxy' ? true : false;
+	}
+
+	/**
+	 * If PROXY AUTH is configured, then start it
+	 */
+	private function startProxy($resource,$method) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$rootdse = $this->getRootDSE();
+
+		if (! (isset($rootdse['supportedcontrol']) && in_array('2.16.840.1.113730.3.4.18',$rootdse['supportedcontrol']))) {
+			system_message(array(
+				'title'=>sprintf('%s %s',_('Unable to start proxy connection'),$this->getName()),
+				'body'=>sprintf('%s: %s',_('Error'),_('Your LDAP server doesnt seem to support this control')),
+				'type'=>'error'));
+
+			return false;
+		}
+
+		$filter = '(&';
+		$dn = '';
+
+		$missing = false;
+		foreach ($this->getValue('proxy','attr') as $attr => $var) {
+			if (! isset($_SERVER[$var])) {
+				system_message(array(
+					'title'=>sprintf('%s %s',_('Unable to start proxy connection'),$this->getName()),
+					'body'=>sprintf('%s: %s (%s)',_('Error'),_('Attribute doesnt exist'),$var),
+					'type'=>'error'));
+
+				$missing = true;
+
+			} else {
+				if ($attr == 'dn') {
+					$dn = $var;
+
+					break;
+
+				} else
+					$filter .= sprintf('(%s=%s)',$attr,$_SERVER[$var]);
+			}
+		}
+
+		if ($missing)
+			return false;
+
+		$filter .= ')';
+
+		if (! $dn) {
+			$query['filter'] = $filter;
+
+			foreach ($this->getBaseDN() as $base) {
+				$query['base'] = $base;
+
+				if ($search = $this->query($query,$method))
+					break;
+			}
+
+			if (count($search) != 1) {
+				system_message(array(
+					'title'=>sprintf('%s %s',_('Unable to start proxy connection'),$this->getName()),
+					'body'=>sprintf('%s: %s (%s)',_('Error'),_('Search for DN returned the incorrect number of results'),count($search)),
+					'type'=>'error'));
+
+				return false;
+			}
+
+			$search = array_pop($search);
+			$dn = $search['dn'];
+		}
+
+		$ctrl = array(
+			'oid'=>'2.16.840.1.113730.3.4.18',
+			'value'=>sprintf('dn:%s',$dn),
+			'iscritical' => true);
+
+		if (! ldap_set_option($resource,LDAP_OPT_SERVER_CONTROLS,array($ctrl))) {
+			system_message(array(
+				'title'=>sprintf('%s %s',_('Unable to start proxy connection'),$this->getName()),
+				'body'=>sprintf('%s: %s (%s) for %s',_('Error'),$this->getErrorMessage($method),$this->getErrorNum($method),$method),
+				'type'=>'error'));
+
+			return false;
+		}
+
+		$_SESSION['USER'][$this->index][$method]['proxy'] = blowfish_encrypt($dn);
+
+		return true;
+	}
+
+	/**
+	 * Modify attributes of a DN
+	 */
+	public function modify($dn,$attrs,$method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# We need to supress the error here - programming should detect and report it.
+		return @ldap_mod_replace($this->connect($method),$dn,$attrs);
+	}
+
+	/**
+	 * Gets the root DN of the specified LDAPServer, or null if it
+	 * can't find it (ie, the server won't give it to us, or it isnt
+	 * specified in the configuration file).
+	 *
+	 * Tested with OpenLDAP 2.0, Netscape iPlanet, and Novell eDirectory 8.7 (nldap.com)
+	 * Please report any and all bugs!!
+	 *
+	 * Please note: On FC systems, it seems that php_ldap uses /etc/openldap/ldap.conf in
+	 * the search base if it is blank - so edit that file and comment out the BASE line.
+	 *
+	 * @param string Which connection method resource to use
+	 * @return array dn|null The root DN of the server on success (string) or null on error.
+	 * @todo Sort the entries, so that they are in the correct DN order.
+	 */
+	public function getBaseDN($method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		static $CACHE;
+
+		$method = $this->getMethod($method);
+		$result = array();
+
+		if (isset($CACHE[$this->index][$method]))
+			return $CACHE[$this->index][$method];
+
+		# If the base is set in the configuration file, then just return that.
+		if (count($this->getValue('server','base'))) {
+			if (DEBUG_ENABLED)
+				debug_log('Return BaseDN from Config [%s]',17,0,__FILE__,__LINE__,__METHOD__,implode('|',$this->getValue('server','base')));
+
+			$CACHE[$this->index][$method] = $this->getValue('server','base');
+
+		# We need to figure it out.
+		} else {
+			if (DEBUG_ENABLED)
+				debug_log('Connect to LDAP to find BaseDN',80,0,__FILE__,__LINE__,__METHOD__);
+
+			# Set this to empty, in case we loop back here looking for the baseDNs
+			$CACHE[$this->index][$method] = array();
+
+			$results = $this->getDNAttrValues('',$method);
+
+			if (isset($results['namingcontexts'])) {
+				if (DEBUG_ENABLED)
+					debug_log('LDAP Entries:%s',80,0,__FILE__,__LINE__,__METHOD__,implode('|',$results['namingcontexts']));
+
+				$result = $results['namingcontexts'];
+			}
+
+			$CACHE[$this->index][$method] = $result;
+		}
+
+		return $CACHE[$this->index][$method];
+	}
+
+	/**
+	 * Gets whether an entry exists based on its DN. If the entry exists,
+	 * returns true. Otherwise returns false.
+	 *
+	 * @param string The DN of the entry of interest.
+	 * @param string Which connection method resource to use
+	 * @return boolean
+	 */
+	public function dnExists($dn,$method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$results = $this->getDNAttrValues($dn,$method);
+
+		if ($results)
+			return $results;
+		else
+			return false;
+	}
+
+	/**
+	 * Given a DN string, this returns the top container portion of the string.
+	 *
+	 * @param string The DN whose container string to return.
+	 * @return string The container
+	 */
+	public function getContainerTop($dn) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$return = $dn;
+
+		foreach ($this->getBaseDN() as $base) {
+			if (preg_match("/${base}$/i",$dn)) {
+				$return = $base;
+				break;
+			}
+		}
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
+
+		return $return;
+	}
+
+	/**
+	 * Given a DN string and a path like syntax, this returns the parent container portion of the string.
+	 *
+	 * @param string The DN whose container string to return.
+	 * @param string Either '/', '.' or something like '../../'
+	 * @return string The container
+	 */
+	public function getContainerPath($dn,$path='..') {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$top = $this->getContainerTop($dn);
+
+		if ($path[0] == '/') {
+			$dn = $top;
+			$path = substr($path,1);
+
+		} elseif ($path == '.') {
+			return $dn;
+		}
+
+		$parenttree = explode('/',$path);
+
+		foreach ($parenttree as $key => $value) {
+			if ($value == '..') {
+				if ($this->getContainer($dn))
+					$dn = $this->getContainer($dn);
+
+				if ($dn == $top)
+					break;
+
+			} elseif($value)
+				$dn = sprintf('%s,%s',$value,$dn);
+
+			else
+				break;
+		}
+
+		if (! $dn) {
+			debug_dump(array(__METHOD__,'dn'=>$dn,'path'=>$path));
+			debug_dump_backtrace('Container is empty?',1);
+		}
+
+		return $dn;
+	}
+
+	/**
+	 * Given a DN string, this returns the parent container portion of the string.
+	 * For example. given 'cn=Manager,dc=example,dc=com', this function returns
+	 * 'dc=example,dc=com'.
+	 *
+	 * @param string The DN whose container string to return.
+	 * @return string The container
+	 */
+	public function getContainer($dn) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$parts = $this->explodeDN($dn);
+
+		if (count($parts) <= 1)
+			$return = null;
+
+		else {
+			$return = $parts[1];
+
+			for ($i=2;$i
+	 *	dc=example,dc=com
+	 *		ou=People
+	 *			cn=Dave
+	 *			cn=Fred
+	 *			cn=Joe
+	 *		ou=More People
+	 *			cn=Mark
+	 *			cn=Bob
+	 * 
+	 *
+	 * Calling getContainerContents("ou=people,dc=example,dc=com")
+	 * would return the following list:
+	 *
+	 * 
+	 *	cn=Dave
+	 *	cn=Fred
+	 *	cn=Joe
+	 *	ou=More People
+	 * 
+	 *
+	 * @param string The DN of the entry whose children to return.
+	 * @param string Which connection method resource to use
+	 * @param int (optional) The maximum number of entries to return.
+	 *            If unspecified, no limit is applied to the number of entries in the returned.
+	 * @param string (optional) An LDAP filter to apply when fetching children, example: "(objectClass=inetOrgPerson)"
+	 * @param constant (optional) The LDAP deref setting to use in the query
+	 * @return array An array of DN strings listing the immediate children of the specified entry.
+	 */
+	public function getContainerContents($dn,$method=null,$size_limit=0,$filter='(objectClass=*)',$deref=LDAP_DEREF_NEVER) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$return = array();
+
+		$query = array();
+		$query['base'] = $this->escapeDN($dn);
+		$query['attrs'] = array('dn');
+		$query['filter'] = $filter;
+		$query['deref'] = $deref;
+		$query['scope'] = 'one';
+		$query['size_limit'] = $size_limit;
+		$results = $this->query($query,$method);
+
+		if ($results) {
+			foreach ($results as $index => $entry) {
+				$child_dn = $entry['dn'];
+				array_push($return,$child_dn);
+			}
+		}
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
+
+		# Sort the results
+		asort($return);
+
+		return $return;
+	}
+
+	/**
+	 * Explode a DN into an array of its RDN parts.
+	 *
+	 * @param string The DN to explode.
+	 * @param int (optional) Whether to include attribute names (see http://php.net/ldap_explode_dn for details)
+	 *
+	 * @return array An array of RDN parts of this format:
+	 * 
+	 *	Array
+	 *		(
+	 *			[0] => uid=ppratt
+	 *			[1] => ou=People
+	 *			[2] => dc=example
+	 *			[3] => dc=com
+	 *		)
+	 * 
+	 *
+	 * NOTE: When a multivalue RDN is passed to ldap_explode_dn, the results returns with 'value + value';
+	 */
+	private function explodeDN($dn,$with_attributes=0) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		static $CACHE;
+
+		if (isset($CACHE['explode'][$dn][$with_attributes])) {
+			if (DEBUG_ENABLED)
+				debug_log('Return CACHED result (%s) for (%s)',1,0,__FILE__,__LINE__,__METHOD__,
+					$CACHE['explode'][$dn][$with_attributes],$dn);
+
+			return $CACHE['explode'][$dn][$with_attributes];
+		}
+
+		$dn = addcslashes($dn,'<>+";');
+
+		# split the dn
+		$result[0] = ldap_explode_dn($this->escapeDN($dn),0);
+		$result[1] = ldap_explode_dn($this->escapeDN($dn),1);
+		if (! $result[$with_attributes]) {
+			if (DEBUG_ENABLED)
+				debug_log('Returning NULL - NO result.',1,0,__FILE__,__LINE__,__METHOD__);
+
+			return array();
+		}
+
+		# Remove our count value that ldap_explode_dn returns us.
+		unset($result[0]['count']);
+		unset($result[1]['count']);
+
+		# Record the forward and reverse entries in the cache.
+		foreach ($result as $key => $value) {
+			# translate hex code into ascii for display
+			$result[$key] = $this->unescapeDN($value);
+
+			$CACHE['explode'][implode(',',$result[0])][$key] = $result[$key];
+			$CACHE['explode'][implode(',',array_reverse($result[0]))][$key] = array_reverse($result[$key]);
+		}
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$result[$with_attributes]);
+
+		return $result[$with_attributes];
+	}
+
+	/**
+	 * Parse a DN and escape any special characters
+	 */
+	protected function escapeDN($dn) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		if (! trim($dn))
+			return $dn;
+
+		# Check if the RDN has a comma and escape it.
+		while (preg_match('/([^\\\\]),(\s*[^=]*\s*),/',$dn))
+			$dn = preg_replace('/([^\\\\]),(\s*[^=]*\s*),/','$1\\\\2C$2,',$dn);
+
+		$dn = preg_replace('/([^\\\\]),(\s*[^=]*\s*)([^,])$/','$1\\\\2C$2$3',$dn);
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$dn);
+
+		return $dn;
+	}
+
+	/**
+	 * Parse a DN and unescape any special characters
+	 */
+	private function unescapeDN($dn) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		if (is_array($dn)) {
+			$a = array();
+			foreach ($dn as $key => $rdn)
+				$a[$key] = preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$rdn);
+
+			return $a;
+
+		} else
+			return preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$dn);
+	}
+
+	public function getRootDSE($method=null) {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$query = array();
+		$query['base'] = '';
+		$query['scope'] = 'base';
+		$query['attrs'] = $this->getValue('server','root_dse_attributes');
+		$query['baseok'] = true;
+		$results = $this->query($query,$method);
+
+		if (is_array($results) && count($results) == 1)
+			return array_change_key_case(array_pop($results));
+		else
+			return array();
+	}
+
+	/** Schema Methods **/
+	/**
+	 * This function will query the ldap server and request the subSchemaSubEntry which should be the Schema DN.
+	 *
+	 * If we cant connect to the LDAP server, we'll return false.
+	 * If we can connect but cant get the entry, then we'll return null.
+	 *
+	 * @param string Which connection method resource to use
+	 * @param dn The DN to use to obtain the schema
+	 * @return array|false Schema if available, null if its not or false if we cant connect.
+	 */
+	private function getSchemaDN($method=null,$dn='') {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		# If we already got the SchemaDN, then return it.
+		if ($this->_schemaDN)
+			return $this->_schemaDN;
+
+		if (! $this->connect($method))
+			return false;
+
+		$search = @ldap_read($this->connect($method),$dn,'objectclass=*',array('subschemaSubentry'),false,0,10,LDAP_DEREF_NEVER);
+
+		if (DEBUG_ENABLED)
+			debug_log('Search returned (%s)',24,0,__FILE__,__LINE__,__METHOD__,is_resource($search));
+
+		# Fix for broken ldap.conf configuration.
+		if (! $search && ! $dn) {
+			if (DEBUG_ENABLED)
+				debug_log('Trying to find the DN for "broken" ldap.conf',80,0,__FILE__,__LINE__,__METHOD__);
+
+			if (isset($this->_baseDN)) {
+				foreach ($this->_baseDN as $base) {
+					$search = @ldap_read($this->connect($method),$base,'objectclass=*',array('subschemaSubentry'),false,0,10,LDAP_DEREF_NEVER);
+
+					if (DEBUG_ENABLED)
+						debug_log('Search returned (%s) for base (%s)',24,0,__FILE__,__LINE__,__METHOD__,
+							is_resource($search),$base);
+
+					if ($search)
+						break;
+				}
+			}
+		}
+
+		if (! $search)
+			return null;
+
+		if (! @ldap_count_entries($this->connect($method),$search)) {
+			if (DEBUG_ENABLED)
+				debug_log('Search returned 0 entries. Returning NULL',25,0,__FILE__,__LINE__,__METHOD__);
+
+			return null;
+		}
+
+		$entries = @ldap_get_entries($this->connect($method),$search);
+
+		if (DEBUG_ENABLED)
+			debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$entries);
+
+		if (! $entries || ! is_array($entries))
+			return null;
+
+		$entry = isset($entries[0]) ? $entries[0] : false;
+		if (! $entry) {
+			if (DEBUG_ENABLED)
+				debug_log('Entry is false, Returning NULL',80,0,__FILE__,__LINE__,__METHOD__);
+
+			return null;
+		}
+
+		$sub_schema_sub_entry = isset($entry[0]) ? $entry[0] : false;
+		if (! $sub_schema_sub_entry) {
+			if (DEBUG_ENABLED)
+				debug_log('Sub Entry is false, Returning NULL',80,0,__FILE__,__LINE__,__METHOD__);
+
+			return null;
+		}
+
+		$this->_schemaDN = isset($entry[$sub_schema_sub_entry][0]) ? $entry[$sub_schema_sub_entry][0] : false;
+
+		if (DEBUG_ENABLED)
+			debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->_schemaDN);
+
+		return $this->_schemaDN;
+	}
+
+	/**
+	 * Fetches the raw schema array for the subschemaSubentry of the server. Note,
+	 * this function has grown many hairs to accomodate more LDAP servers. It is
+	 * needfully complicated as it now supports many popular LDAP servers that
+	 * don't necessarily expose their schema "the right way".
+	 *
+	 * Please note: On FC systems, it seems that php_ldap uses /etc/openldap/ldap.conf in
+	 * the search base if it is blank - so edit that file and comment out the BASE line.
+	 *
+	 * @param string Which connection method resource to use
+	 * @param string A string indicating which type of schema to
+	 *		fetch. Five valid values: 'objectclasses', 'attributetypes',
+	 *		'ldapsyntaxes', 'matchingruleuse', or 'matchingrules'.
+	 *		Case insensitive.
+	 * @param dn (optional) This paremeter is the DN of the entry whose schema you
+	 * 		would like to fetch. Entries have the option of specifying
+	 * 		their own subschemaSubentry that points to the DN of the system
+	 * 		schema entry which applies to this attribute. If unspecified,
+	 *		this will try to retrieve the schema from the RootDSE subschemaSubentry.
+	 *		Failing that, we use some commonly known schema DNs. Default
+	 *		value is the Root DSE DN (zero-length string)
+	 * @return array an array of strings of this form:
+	 *	Array (
+	 *		[0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ...
+	 *		[1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ...
+	 *	etc.
+	 */
+	private function getRawSchema($method,$schema_to_fetch,$dn='') {
+		if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+			debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
+
+		$valid_schema_to_fetch = array('objectclasses','attributetypes','ldapsyntaxes','matchingrules','matchingruleuse');
+
+		if (! $this->connect($method) || $this->noconnect)
+			return false;
+
+		# error checking
+		$schema_to_fetch = strtolower($schema_to_fetch);
+
+		if (! is_null($this->_schema_entries) && isset($this->_schema_entries[$schema_to_fetch])) {
+			$schema = $this->_schema_entries[$schema_to_fetch];
+
+			if (DEBUG_ENABLED)
+				debug_log('Returning CACHED (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema);
+
+			return $schema;
+		}
+
+		# This error message is not localized as only developers should ever see it
+		if (! in_array($schema_to_fetch,$valid_schema_to_fetch))
+			error(sprintf('Bad parameter provided to function to %s::getRawSchema(). "%s" is not valid for the schema_to_fetch parameter.',
+					get_class($this),$schema_to_fetch),'error','index.php');
+
+		# Try to get the schema DN from the specified entry.
+		$schema_dn = $this->getSchemaDN($method,$dn);
+
+		# Do we need to try again with the Root DSE?
+		if (! $schema_dn && trim($dn))
+			$schema_dn = $this->getSchemaDN($method,'');
+
+		# Store the eventual schema retrieval in $schema_search
+		$schema_search = null;
+
+		if ($schema_dn) {
+			if (DEBUG_ENABLED)
+				debug_log('Using Schema DN (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_dn);
+
+			foreach (array('(objectClass=*)','(objectClass=subschema)') as $schema_filter) {
+				if (DEBUG_ENABLED)
+					debug_log('Looking for schema with Filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_filter);
+
+				$schema_search = @ldap_read($this->connect($method),$schema_dn,$schema_filter,array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER);
+
+				if (is_null($schema_search))
+					continue;
+
+				$schema_entries = @ldap_get_entries($this->connect($method),$schema_search);
+
+				if (DEBUG_ENABLED)
+					debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries);
+
+				if (is_array($schema_entries) && isset($schema_entries['count']) && $schema_entries['count']) {
+					if (DEBUG_ENABLED)
+						debug_log('Found schema with (DN:%s) (FILTER:%s) (ATTR:%s)',24,0,__FILE__,__LINE__,__METHOD__,
+							$schema_dn,$schema_filter,$schema_to_fetch);
+
+					break;
+				}
+
+				if (DEBUG_ENABLED)
+					debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_filter);
+
+				unset($schema_entries);
+				$schema_search = null;
+			}
+		}
+
+		/* Second chance: If the DN or Root DSE didn't give us the subschemaSubentry, ie $schema_search
+		 * is still null, use some common subSchemaSubentry DNs as a work-around. */
+		if (is_null($schema_search)) {
+			if (DEBUG_ENABLED)
+				debug_log('Attempting work-arounds for "broken" LDAP servers...',24,0,__FILE__,__LINE__,__METHOD__);
+
+			foreach ($this->getBaseDN() as $base) {
+				$ldap['W2K3 AD'][expand_dn_with_base($base,'cn=Aggregate,cn=Schema,cn=configuration,')] = '(objectClass=*)';
+				$ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,cn=configuration,')] = '(objectClass=*)';
+				$ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,ou=Admin,')] = '(objectClass=*)';
+			}
+
+			# OpenLDAP and Novell
+			$ldap['OpenLDAP']['cn=subschema'] = '(objectClass=*)';
+
+			foreach ($ldap as $ldap_server_name => $ldap_options) {
+				foreach ($ldap_options as $ldap_dn => $ldap_filter) {
+					if (DEBUG_ENABLED)
+						debug_log('Attempting [%s] (%s) (%s)
',24,0,__FILE__,__LINE__,__METHOD__, + $ldap_server_name,$ldap_dn,$ldap_filter); + + $schema_search = @ldap_read($this->connect($method),$ldap_dn,$ldap_filter,array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER); + if (is_null($schema_search)) + continue; + + $schema_entries = @ldap_get_entries($this->connect($method),$schema_search); + + if (DEBUG_ENABLED) + debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries); + + if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) { + if (DEBUG_ENABLED) + debug_log('Found schema with filter of (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter); + + break; + } + + if (DEBUG_ENABLED) + debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter); + + unset($schema_entries); + $schema_search = null; + } + if ($schema_search) + break; + } + } + + if (is_null($schema_search)) { + /* Still cant find the schema, try with the RootDSE + * Attempt to pull schema from Root DSE with scope "base", or + * Attempt to pull schema from Root DSE with scope "one" (work-around for Isode M-Vault X.500/LDAP) */ + foreach (array('base','one') as $ldap_scope) { + if (DEBUG_ENABLED) + debug_log('Attempting to find schema with scope (%s), filter (objectClass=*) and a blank base.',24,0,__FILE__,__LINE__,__METHOD__, + $ldap_scope); + + switch ($ldap_scope) { + case 'base': + $schema_search = @ldap_read($this->connect($method),'','(objectClass=*)',array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER); + break; + + case 'one': + $schema_search = @ldap_list($this->connect($method),'','(objectClass=*)',array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER); + break; + } + + if (is_null($schema_search)) + continue; + + $schema_entries = @ldap_get_entries($this->connect($method),$schema_search); + if (DEBUG_ENABLED) + debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries); + + if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) { + if (DEBUG_ENABLED) + debug_log('Found schema with filter of (%s)',24,0,__FILE__,__LINE__,__METHOD__,'(objectClass=*)'); + + break; + } + + if (DEBUG_ENABLED) + debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,'(objectClass=*)'); + + unset($schema_entries); + $schema_search = null; + } + } + + $schema_error_message = 'Please contact the phpLDAPadmin developers and let them know:
  • Which LDAP server you are running, including which version
  • What OS it is running on
  • Which version of PHP
  • As well as a link to some documentation that describes how to obtain the SCHEMA information

We\'ll then add support for your LDAP server in an upcoming release.'; + $schema_error_message_array = array('objectclasses','attributetypes'); + + # Shall we just give up? + if (is_null($schema_search)) { + # We need to have objectclasses and attribues, so display an error, asking the user to get us this information. + if (in_array($schema_to_fetch,$schema_error_message_array)) + system_message(array( + 'title'=>sprintf('%s (%s)',_('Our attempts to find your SCHEMA have failed'),$schema_to_fetch), + 'body'=>sprintf('%s: %s',_('Error'),$schema_error_message), + 'type'=>'error')); + else + if (DEBUG_ENABLED) + debug_log('Returning because schema_search is NULL ()',25,0,__FILE__,__LINE__,__METHOD__); + + # We'll set this, so if we return here our cache will return the known false. + $this->_schema_entries[$schema_to_fetch] = false; + return false; + } + + if (! $schema_entries) { + $return = false; + if (DEBUG_ENABLED) + debug_log('Returning false since ldap_get_entries() returned false.',25,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; + } + + if(! isset($schema_entries[0][$schema_to_fetch])) { + if (in_array($schema_to_fetch,$schema_error_message_array)) { + error(sprintf('Our attempts to find your SCHEMA for "%s" has return UNEXPECTED results.

(We expected a "%s" in the $schema array but it wasnt there.)

%s

Dump of $schema_search:
%s
', + $schema_to_fetch,gettype($schema_search),$schema_error_message,serialize($schema_entries)),'error','index.php'); + + } else { + $return = false; + + if (DEBUG_ENABLED) + debug_log('Returning because (%s) isnt in the schema array. (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema_to_fetch,$return); + + return $return; + } + } + + /* Make a nice array of this form: + Array ( + [0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ...)" + [1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ...)" + etc.) */ + + $schema = $schema_entries[0][$schema_to_fetch]; + unset($schema['count']); + $this->_schema_entries[$schema_to_fetch] = $schema; + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema); + + return $schema; + } + + /** + * Gets a single ObjectClass object specified by name. + * + * @param string $oclass_name The name of the objectClass to fetch. + * @param string $dn (optional) It is easier to fetch schema if a DN is provided + * which defines the subschemaSubEntry attribute (all entries should). + * + * @return ObjectClass The specified ObjectClass object or false on error. + * + * @see ObjectClass + * @see SchemaObjectClasses + */ + public function getSchemaObjectClass($oclass_name,$method=null,$dn='') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $oclass_name = strtolower($oclass_name); + $socs = $this->SchemaObjectClasses($method,$dn); + + # Default return value + $return = false; + + if (isset($socs[$oclass_name])) + $return = $socs[$oclass_name]; + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; + } + + /** + * Gets a single AttributeType object specified by name. + * + * @param string $oclass_name The name of the AttributeType to fetch. + * @param string $dn (optional) It is easier to fetch schema if a DN is provided + * which defines the subschemaSubEntry attribute (all entries should). + * + * @return AttributeType The specified AttributeType object or false on error. + * + * @see AttributeType + * @see SchemaAttributes + */ + public function getSchemaAttribute($attr_name,$method=null,$dn='') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attr_name = strtolower($attr_name); + $sattrs = $this->SchemaAttributes($method,$dn); + + # Default return value + $return = false; + + if (isset($sattrs[$attr_name])) + $return = $sattrs[$attr_name]; + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; + } + + /** + * Gets an associative array of ObjectClass objects for the specified + * server. Each array entry's key is the name of the objectClass + * in lower-case and the value is an ObjectClass object. + * + * @param string $dn (optional) It is easier to fetch schema if a DN is provided + * which defines the subschemaSubEntry attribute (all entries should). + * + * @return array An array of ObjectClass objects. + * + * @see ObjectClass + * @see getSchemaObjectClass + */ + public function SchemaObjectClasses($method=null,$dn='') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Set default return + $return = null; + + if ($return = get_cached_item($this->index,'schema','objectclasses')) { + if (DEBUG_ENABLED) + debug_log('Returning CACHED [%s] (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'objectclasses'); + + return $return; + } + + $raw = $this->getRawSchema($method,'objectclasses',$dn); + + if ($raw) { + # Build the array of objectClasses + $return = array(); + + foreach ($raw as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $object_class = new ObjectClass($line,$this); + $return[$object_class->getName()] = $object_class; + } + + # Now go through and reference the parent/child relationships + foreach ($return as $oclass) + foreach ($oclass->getSupClasses() as $parent_name) + if (isset($return[strtolower($parent_name)])) + $return[strtolower($parent_name)]->addChildObjectClass($oclass->getName(false)); + + ksort($return); + + # cache the schema to prevent multiple schema fetches from LDAP server + set_cached_item($this->index,'schema','objectclasses',$return); + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; + } + + /** + * Gets an associative array of AttributeType objects for the specified + * server. Each array entry's key is the name of the attributeType + * in lower-case and the value is an AttributeType object. + * + * @param string $dn (optional) It is easier to fetch schema if a DN is provided + * which defines the subschemaSubEntry attribute (all entries should). + * + * @return array An array of AttributeType objects. + */ + public function SchemaAttributes($method=null,$dn='') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Set default return + $return = null; + + if ($return = get_cached_item($this->index,'schema','attributes')) { + if (DEBUG_ENABLED) + debug_log('(): Returning CACHED [%s] (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'attributes'); + + return $return; + } + + $raw = $this->getRawSchema($method,'attributeTypes',$dn); + + if ($raw) { + # build the array of attribueTypes + $syntaxes = $this->SchemaSyntaxes($method,$dn); + $attrs = array(); + + /** + * bug 856832: create two arrays - one indexed by name (the standard + * $attrs array above) and one indexed by oid (the new $attrs_oid array + * below). This will help for directory servers, like IBM's, that use OIDs + * in their attribute definitions of SUP, etc + */ + $attrs_oid = array(); + foreach ($raw as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $attr = new AttributeType($line); + if (isset($syntaxes[$attr->getSyntaxOID()])) { + $syntax = $syntaxes[$attr->getSyntaxOID()]; + $attr->setType($syntax->getDescription()); + } + $attrs[$attr->getName()] = $attr; + + /** + * bug 856832: create an entry in the $attrs_oid array too. This + * will be a ref to the $attrs entry for maintenance and performance + * reasons + */ + $attrs_oid[$attr->getOID()] = &$attrs[$attr->getName()]; + } + + # go back and add data from aliased attributeTypes + foreach ($attrs as $name => $attr) { + $aliases = $attr->getAliases(); + + if (is_array($aliases) && count($aliases) > 0) { + /* foreach of the attribute's aliases, create a new entry in the attrs array + * with its name set to the alias name, and all other data copied.*/ + foreach ($aliases as $alias_attr_name) { + $new_attr = clone $attr; + + $new_attr->setName($alias_attr_name); + $new_attr->addAlias($attr->getName(false)); + $new_attr->removeAlias($alias_attr_name); + $new_attr_key = strtolower($alias_attr_name); + $attrs[$new_attr_key] = $new_attr; + } + } + } + + # go back and add any inherited descriptions from parent attributes (ie, cn inherits name) + foreach ($attrs as $key => $attr) { + $sup_attr_name = $attr->getSupAttribute(); + $sup_attr = null; + + if (trim($sup_attr_name)) { + + /* This loop really should traverse infinite levels of inheritance (SUP) for attributeTypes, + * but just in case we get carried away, stop at 100. This shouldn't happen, but for + * some weird reason, we have had someone report that it has happened. Oh well.*/ + $i = 0; + while ($i++<100 /** 100 == INFINITY ;) */) { + + if (isset($attrs_oid[$sup_attr_name])) { + $attr->setSupAttribute($attrs_oid[$sup_attr_name]->getName()); + $sup_attr_name = $attr->getSupAttribute(); + } + + if (! isset($attrs[strtolower($sup_attr_name)])){ + error(sprintf('Schema error: attributeType "%s" inherits from "%s", but attributeType "%s" does not exist.', + $attr->getName(),$sup_attr_name,$sup_attr_name),'error','index.php'); + return; + } + + $sup_attr = $attrs[strtolower($sup_attr_name)]; + $sup_attr_name = $sup_attr->getSupAttribute(); + + # Does this superior attributeType not have a superior attributeType? + if (is_null($sup_attr_name) || strlen(trim($sup_attr_name)) == 0) { + + /* Since this attribute's superior attribute does not have another superior + * attribute, clone its properties for this attribute. Then, replace + * those cloned values with those that can be explicitly set by the child + * attribute attr). Save those few properties which the child can set here:*/ + $tmp_name = $attr->getName(false); + $tmp_oid = $attr->getOID(); + $tmp_sup = $attr->getSupAttribute(); + $tmp_aliases = $attr->getAliases(); + $tmp_single_val = $attr->getIsSingleValue(); + $tmp_desc = $attr->getDescription(); + + /* clone the SUP attributeType and populate those values + * that were set by the child attributeType */ + $attr = clone $sup_attr; + + $attr->setOID($tmp_oid); + $attr->setName($tmp_name); + $attr->setSupAttribute($tmp_sup); + $attr->setAliases($tmp_aliases); + $attr->setDescription($tmp_desc); + + /* only overwrite the SINGLE-VALUE property if the child explicitly sets it + * (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */ + if ($tmp_single_val) + $attr->setIsSingleValue(true); + + /* replace this attribute in the attrs array now that we have populated + new values therein */ + $attrs[$key] = $attr; + + # very important: break out after we are done with this attribute + $sup_attr_name = null; + $sup_attr = null; + break; + } + } + } + } + + ksort($attrs); + + # Add the used in and required_by values. + $socs = $this->SchemaObjectClasses($method); + if (! is_array($socs)) + return array(); + + foreach ($socs as $object_class) { + $must_attrs = $object_class->getMustAttrNames(); + $may_attrs = $object_class->getMayAttrNames(); + $oclass_attrs = array_unique(array_merge($must_attrs,$may_attrs)); + + # Add Used In. + foreach ($oclass_attrs as $attr_name) + if (isset($attrs[strtolower($attr_name)])) + $attrs[strtolower($attr_name)]->addUsedInObjectClass($object_class->getName(false)); + + # Add Required By. + foreach ($must_attrs as $attr_name) + if (isset($attrs[strtolower($attr_name)])) + $attrs[strtolower($attr_name)]->addRequiredByObjectClass($object_class->getName(false)); + + # Force May + foreach ($object_class->getForceMayAttrs() as $attr_name) + if (isset($attrs[strtolower($attr_name->name)])) + $attrs[strtolower($attr_name->name)]->setForceMay(); + } + + $return = $attrs; + + # cache the schema to prevent multiple schema fetches from LDAP server + set_cached_item($this->index,'schema','attributes',$return); + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; + } + + /** + * Returns an array of MatchingRule objects for the specified server. + * The key of each entry is the OID of the matching rule. + */ + public function MatchingRules($method=null,$dn='') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Set default return + $return = null; + + if ($return = get_cached_item($this->index,'schema','matchingrules')) { + if (DEBUG_ENABLED) + debug_log('Returning CACHED [%s] (%s).',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'matchingrules'); + + return $return; + } + + # build the array of MatchingRule objects + $raw = $this->getRawSchema($method,'matchingRules',$dn); + + if ($raw) { + $rules = array(); + + foreach ($raw as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $rule = new MatchingRule($line); + $key = $rule->getName(); + $rules[$key] = $rule; + } + + ksort($rules); + + /* For each MatchingRuleUse entry, add the attributes who use it to the + * MatchingRule in the $rules array.*/ + $raw = $this->getRawSchema($method,'matchingRuleUse'); + + if ($raw != false) { + foreach ($raw as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $rule_use = new MatchingRuleUse($line); + $key = $rule_use->getName(); + + if (isset($rules[$key])) + $rules[$key]->setUsedByAttrs($rule_use->getUsedByAttrs()); + } + + } else { + /* No MatchingRuleUse entry in the subschema, so brute-forcing + * the reverse-map for the "$rule->getUsedByAttrs()" data.*/ + $sattrs = $this->SchemaAttributes($method,$dn); + if (is_array($sattrs)) + foreach ($sattrs as $attr) { + $rule_key = strtolower($attr->getEquality()); + + if (isset($rules[$rule_key])) + $rules[$rule_key]->addUsedByAttr($attr->getName(false)); + } + } + + $return = $rules; + + # cache the schema to prevent multiple schema fetches from LDAP server + set_cached_item($this->index,'schema','matchingrules',$return); + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; + } + + /** + * Returns an array of Syntax objects that this LDAP server uses mapped to + * their descriptions. The key of each entry is the OID of the Syntax. + */ + public function SchemaSyntaxes($method=null,$dn='') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Set default return + $return = null; + + if ($return = get_cached_item($this->index,'schema','syntaxes')) { + if (DEBUG_ENABLED) + debug_log('Returning CACHED [%s] (%s).',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'syntaxes'); + + return $return; + } + + $raw = $this->getRawSchema($method,'ldapSyntaxes',$dn); + + if ($raw) { + # build the array of attributes + $return = array(); + + foreach ($raw as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $syntax = new Syntax($line); + $key = strtolower(trim($syntax->getOID())); + + if (! $key) + continue; + + $return[$key] = $syntax; + } + + ksort($return); + + # cache the schema to prevent multiple schema fetches from LDAP server + set_cached_item($this->index,'schema','syntaxes',$return); + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; + } + + /** + * This function determines if the specified attribute is contained in the force_may list + * as configured in config.php. + * + * @return boolean True if the specified attribute is in the $force_may list and false + * otherwise. + */ + function isForceMay($attr_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return in_array($attr_name,$this->force_may); + } + + /** + * Much like getDNAttrValues(), but only returns the values for + * one attribute of an object. Example calls: + * + * + * print_r(getDNAttrValue('cn=Bob,ou=people,dc=example,dc=com','sn')); + * Array ( + * [0] => Smith + * ) + * + * print_r(getDNAttrValue('cn=Bob,ou=people,dc=example,dc=com','objectClass')); + * Array ( + * [0] => top + * [1] => person + * ) + * + * + * @param string The distinguished name (DN) of the entry whose attributes/values to fetch. + * @param string The attribute whose value(s) to return (ie, "objectClass", "cn", "userPassword") + * @param string Which connection method resource to use + * @param constant For aliases and referrals, this parameter specifies whether to + * follow references to the referenced DN or to fetch the attributes for + * the referencing DN. See http://php.net/ldap_search for the 4 valid + * options. + * @return array + * @see getDNAttrValues + * @todo Caching these values may be problematic with multiple calls and different deref values. + */ + public function getDNAttrValue($dn,$attr,$method=null,$deref=LDAP_DEREF_NEVER) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Ensure our attr is in lowercase + $attr = strtolower($attr); + + $values = $this->getDNAttrValues($dn,$method,$deref); + + if (isset($values[$attr])) + return $values[$attr]; + else + return array(); + } + + /** + * Gets the attributes/values of an entry. Returns an associative array whose + * keys are attribute value names and whose values are arrays of values for + * said attribute. + * + * Optionally, callers may specify true for the parameter + * $lower_case_attr_names to force all keys in the associate array (attribute + * names) to be lower case. + * + * Example of its usage: + * + * print_r(getDNAttrValues('cn=Bob,ou=pepole,dc=example,dc=com') + * Array ( + * [objectClass] => Array ( + * [0] => person + * [1] => top + * ) + * [cn] => Array ( + * [0] => Bob + * ) + * [sn] => Array ( + * [0] => Jones + * ) + * [dn] => Array ( + * [0] => cn=Bob,ou=pepole,dc=example,dc=com + * ) + * ) + * + * + * @param string The distinguished name (DN) of the entry whose attributes/values to fetch. + * @param string Which connection method resource to use + * @param constant For aliases and referrals, this parameter specifies whether to + * follow references to the referenced DN or to fetch the attributes for + * the referencing DN. See http://php.net/ldap_search for the 4 valid + * options. + * @return array + * @see getDNSysAttrs + * @see getDNAttrValue + */ + public function getDNAttrValues($dn,$method=null,$deref=LDAP_DEREF_NEVER,$attrs=array('*','+')) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + static $CACHE; + + $cacheindex = null; + $method = $this->getMethod($method); + + if (in_array('*',$attrs) && in_array('+',$attrs)) + $cacheindex = '&'; + elseif (in_array('+',$attrs)) + $cacheindex = '+'; + elseif (in_array('*',$attrs)) + $cacheindex = '*'; + + if (! is_null($cacheindex) && isset($CACHE[$this->index][$method][$dn][$cacheindex])) { + $results = $CACHE[$this->index][$method][$dn][$cacheindex]; + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$results); + + } else { + $query = array(); + $query['base'] = $this->escapeDN($dn); + $query['scope'] = 'base'; + $query['deref'] = $deref; + $query['attrs'] = $attrs; + $query['baseok'] = true; + $results = $this->query($query,$method); + + if (count($results)) + $results = array_pop($results); + + $results = array_change_key_case($results); + + # Covert all our result key values to an array + foreach ($results as $key => $values) + if (! is_array($results[$key])) + $results[$key] = array($results[$key]); + + # Finally sort the results + ksort($results); + + if (! is_null($cacheindex) && count($results)) + $CACHE[$this->index][$method][$dn][$cacheindex] = $results; + } + + return $results; + } + + /** + * Returns true if the attribute specified is required to take as input a DN. + * Some examples include 'distinguishedName', 'member' and 'uniqueMember'. + * + * @param string $attr_name The name of the attribute of interest (case insensitive) + * @return boolean + */ + function isDNAttr($attr_name,$method=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Simple test first + $dn_attrs = array('aliasedObjectName'); + foreach ($dn_attrs as $dn_attr) + if (strcasecmp($attr_name,$dn_attr) == 0) + return true; + + # Now look at the schema OID + $sattr = $this->getSchemaAttribute($attr_name); + if (! $sattr) + return false; + + $syntax_oid = $sattr->getSyntaxOID(); + if ('1.3.6.1.4.1.1466.115.121.1.12' == $syntax_oid) + return true; + if ('1.3.6.1.4.1.1466.115.121.1.34' == $syntax_oid) + return true; + + $syntaxes = $this->SchemaSyntaxes($method); + if (! isset($syntaxes[$syntax_oid])) + return false; + + $syntax_desc = $syntaxes[ $syntax_oid ]->getDescription(); + if (strpos(strtolower($syntax_desc),'distinguished name')) + return true; + + return false; + } + + /** + * Used to determine if the specified attribute is indeed a jpegPhoto. If the + * specified attribute is one that houses jpeg data, true is returned. Otherwise + * this function returns false. + * + * @param string $attr_name The name of the attribute to test. + * @return boolean + * @see draw_jpeg_photo + */ + function isJpegPhoto($attr_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # easy quick check + if (! strcasecmp($attr_name,'jpegPhoto') || ! strcasecmp($attr_name,'photo')) + return true; + + # go to the schema and get the Syntax OID + $sattr = $this->getSchemaAttribute($attr_name); + if (! $sattr) + return false; + + $oid = $sattr->getSyntaxOID(); + $type = $sattr->getType(); + + if (! strcasecmp($type,'JPEG') || ($oid == '1.3.6.1.4.1.1466.115.121.1.28')) + return true; + + return false; + } + + /** + * Given an attribute name and server ID number, this function returns + * whether the attrbiute contains boolean data. This is useful for + * developers who wish to display the contents of a boolean attribute + * with a drop-down. + * + * @param string $attr_name The name of the attribute to test. + * @return boolean + */ + function isAttrBoolean($attr_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $type = ($sattr = $this->getSchemaAttribute($attr_name)) ? $sattr->getType() : null; + + if (! strcasecmp('boolean',$type) || + ! strcasecmp('isCriticalSystemObject',$attr_name) || + ! strcasecmp('showInAdvancedViewOnly',$attr_name)) + return true; + + else + return false; + } + + /** + * Given an attribute name and server ID number, this function returns + * whether the attribute may contain binary data. This is useful for + * developers who wish to display the contents of an arbitrary attribute + * but don't want to dump binary data on the page. + * + * @param string $attr_name The name of the attribute to test. + * @return boolean + * + * @see isJpegPhoto + */ + function isAttrBinary($attr_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + /** + * Determining if an attribute is binary can be an expensive operation. + * We cache the results for each attr name on each server in the $attr_cache + * to speed up subsequent calls. The $attr_cache looks like this: + * + * Array + * 0 => Array + * 'objectclass' => false + * 'cn' => false + * 'usercertificate' => true + * 1 => Array + * 'jpegphoto' => true + * 'cn' => false + */ + + static $attr_cache; + + $attr_name = strtolower($attr_name); + + if (isset($attr_cache[$this->index][$attr_name])) + return $attr_cache[$this->index][$attr_name]; + + if ($attr_name == 'userpassword') { + $attr_cache[$this->index][$attr_name] = false; + return false; + } + + # Quick check: If the attr name ends in ";binary", then it's binary. + if (strcasecmp(substr($attr_name,strlen($attr_name) - 7),';binary') == 0) { + $attr_cache[$this->index][$attr_name] = true; + return true; + } + + # See what the server schema says about this attribute + $sattr = $this->getSchemaAttribute($attr_name); + if (! is_object($sattr)) { + + /* Strangely, some attributeTypes may not show up in the server + * schema. This behavior has been observed in MS Active Directory.*/ + $type = null; + $syntax = null; + + } else { + $type = $sattr->getType(); + $syntax = $sattr->getSyntaxOID(); + } + + if (strcasecmp($type,'Certificate') == 0 || + strcasecmp($type,'Binary') == 0 || + strcasecmp($attr_name,'usercertificate') == 0 || + strcasecmp($attr_name,'usersmimecertificate') == 0 || + strcasecmp($attr_name,'networkaddress') == 0 || + strcasecmp($attr_name,'objectGUID') == 0 || + strcasecmp($attr_name,'objectSID') == 0 || + strcasecmp($attr_name,'auditingPolicy') == 0 || + strcasecmp($attr_name,'jpegPhoto') == 0 || + $syntax == '1.3.6.1.4.1.1466.115.121.1.10' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.28' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.5' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.8' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.9' + ) { + + $attr_cache[$this->index][$attr_name] = true; + return true; + + } else { + $attr_cache[$this->index][$attr_name] = false; + return false; + } + } + + /** + * This function will test if a user is a member of a group. + * + * Inputs: + * @param string $user membership value that is being checked + * @param dn $group DN to see if user is a member + * @return bool true|false + */ + function userIsMember($user,$group) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $user = strtolower($user); + $group = $this->getDNAttrValues($group); + + # If you are using groupOfNames objectClass + if (array_key_exists('member',$group) && ! is_array($group['member'])) + $group['member'] = array($group['member']); + + if (array_key_exists('member',$group) && + in_array($user,arrayLower($group['member']))) + + return true; + + # If you are using groupOfUniqueNames objectClass + if (array_key_exists('uniquemember',$group) && ! is_array($group['uniquemember'])) + $group['uniquemember'] = array($group['uniquemember']); + + if (array_key_exists('uniquemember',$group) && + in_array($user,arrayLower($group['uniquemember']))) + + return true; + + return false; + } + + /** + * This function will determine if the user is allowed to login based on a filter + */ + protected function userIsAllowedLogin($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $dn = trim(strtolower($dn)); + + if (! $this->getValue('login','allowed_dns')) + return true; + + foreach ($this->getValue('login','allowed_dns') as $login_allowed_dn) { + if (DEBUG_ENABLED) + debug_log('Working through (%s)',80,0,__FILE__,__LINE__,__METHOD__,$login_allowed_dn); + + /* Check if $login_allowed_dn is an ldap search filter + * Is first occurence of 'filter=' (case ensitive) at position 0 ? */ + if (preg_match('/^\([&|]\(/',$login_allowed_dn)) { + $query = array(); + $query['filter'] = $login_allowed_dn; + $query['attrs'] = array('dn'); + + foreach($this->getBaseDN() as $base_dn) { + $query['base'] = $base_dn; + + $results = $this->query($query,null); + + if (DEBUG_ENABLED) + debug_log('Search, Filter [%s], BaseDN [%s] Results [%s]',16,0,__FILE__,__LINE__,__METHOD__, + $query['filter'],$query['base'],$results); + + if ($results) { + $dn_array = array(); + + foreach ($results as $result) + array_push($dn_array,$result['dn']); + + $dn_array = array_unique($dn_array); + + if (count($dn_array)) + foreach ($dn_array as $result_dn) { + if (DEBUG_ENABLED) + debug_log('Comparing with [%s]',80,0,__FILE__,__LINE__,__METHOD__,$result_dn); + + # Check if $result_dn is a user DN + if (strcasecmp($dn,trim(strtolower($result_dn))) == 0) + return true; + + # Check if $result_dn is a group DN + if ($this->userIsMember($dn,$result_dn)) + return true; + } + } + } + } + + # Check if $login_allowed_dn is a user DN + if (strcasecmp($dn,trim(strtolower($login_allowed_dn))) == 0) + return true; + + # Check if $login_allowed_dn is a group DN + if ($this->userIsMember($dn,$login_allowed_dn)) + return true; + } + + return false; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/emuhash_functions.php b/lam/templates/3rdParty/pla/lib/emuhash_functions.php new file mode 100644 index 00000000..7818f7fb --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/emuhash_functions.php @@ -0,0 +1,112 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ******************************************************************************/ + +/******************************************************************************/ +/* Do we have builtin mhash support in this PHP version ? */ +/******************************************************************************/ + +if (! function_exists('mhash') && ! function_exists('mhash_keygen_s2k')) { + $emuhash_emu = array(); + + if (! isset($emuhash_emu['openssl'])) + $emuhash_emu['openssl'] = '/usr/bin/openssl'; + + # Don't create mhash functions if we don't have a working openssl + if (! file_exists($emuhash_emu['openssl'])) + unset($emuhash_emu['openssl']); + + elseif (function_exists('is_executable') && ! is_executable($emuhash_emu['openssl'])) + unset($emuhash_emu['openssl']); + + else { + if (! isset($emuhash_emu['tmpdir'])) + $emuhash_emu['tmpdir'] = '/tmp'; + +/******************************************************************************/ +/* Define constants used in the mhash emulation code. */ +/******************************************************************************/ + + define('MHASH_MD5','md5'); + define('MHASH_SHA1','sha1'); + define('MHASH_RIPEMD160','rmd160'); + +/******************************************************************************/ +/* Functions to emulate parts of php-mash. */ +/******************************************************************************/ + + function openssl_hash($openssl_hash_id,$password_clear) { + global $emuhash_emu; + + if (PHP_VERSION < 6) { + $current_magic_quotes = @get_magic_quotes_runtime(); + @set_magic_quotes_runtime(0); + } + + $tmpfile = tempnam($emuhash_emu['tmpdir'],'emuhash'); + $pwhandle = fopen($tmpfile,'w'); + + if (! $pwhandle) + error(sprintf('Unable to create a temporary file %s to create hashed password',$tmpfile) ,'error','index.php'); + + fwrite($pwhandle,$password_clear); + fclose($pwhandle); + $cmd = sprintf('%s %s -binary <%s',$emuhash_emu['openssl'],$openssl_hash_id,$tmpfile); + $prog = popen($cmd,'r'); + $pass = fread($prog,1024); + pclose($prog); + unlink($tmpfile); + + if (PHP_VERSION < 6) + @set_magic_quotes_runtime($current_magic_quotes); + + return $pass; + } + + function mhash($hash_id,$password_clear) { + switch($hash_id) { + case MHASH_MD5: + $emuhash = openssl_hash(MHASH_MD5,$password_clear); + break; + + case MHASH_SHA1: + $emuhash = openssl_hash(MHASH_SHA1,$password_clear); + break; + + case MHASH_RIPEMD160: + $emuhash = openssl_hash(MHASH_RIPEMD160,$password_clear); + break; + + default: + $emuhash = FALSE; + } + + return $emuhash; + } + + function mhash_keygen_s2k($hash_id,$password_clear,$salt,$bytes) { + return substr(pack('H*',bin2hex(mhash($hash_id,($salt.$password_clear)))),0,$bytes); + } + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/export_functions.php b/lam/templates/3rdParty/pla/lib/export_functions.php new file mode 100644 index 00000000..076b6a6f --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/export_functions.php @@ -0,0 +1,641 @@ +server_id = $server_id; + $this->template_id = $template_id; + + $this->accept(); + } + + static function types() { + $type = array(); + + $details = ExportCSV::getType(); + $type[$details['type']] = $details; + $details = ExportDSML::getType(); + $type[$details['type']] = $details; + $details = ExportLDIF::getType(); + $type[$details['type']] = $details; + $details = ExportVCARD::getType(); + $type[$details['type']] = $details; + + return $type; + } + + private function accept() { + switch($this->template_id) { + case 'CSV': + $this->template = new ExportCSV(); + break; + + case 'DSML': + $this->template = new ExportDSML(); + break; + + case 'LDIF': + $this->template = new ExportLDIF(); + break; + + case 'VCARD': + $this->template = new ExportVCARD(); + break; + + default: + system_message(array( + 'title'=>sprintf('%s %s',_('Unknown Export Type'),$this->template_id), + 'body'=>_('phpLDAPadmin has not been configured for that export type'), + 'type'=>'warn'),'index.php'); + die(); + } + + $this->template->accept(); + } + + public function getTemplate() { + return $this->template; + } +} + +/** + * Export Class + * + * This abstract classes provides all the common methods and variables for the + * custom export classes. + * + * @package phpLDAPadmin + * @subpackage Export + */ +abstract class Export { + # Line Break + protected $br; + # Compress the output + protected $compress; + # Export Results + protected $results; + protected $resultsdata; + protected $items = 0; + + /** + * Return this LDAP Server object + * + * @return object DataStore Server + */ + protected function getServer() { + return $_SESSION[APPCONFIG]->getServer($this->getServerID()); + } + + /** + * Return the LDAP server ID + * + * @return int Server ID + */ + protected function getServerID() { + return get_request('server_id','REQUEST'); + } + + public function accept() { + $server = $this->getServer(); + + # Get the data to be exported + $query = array(); + $base = get_request('dn','REQUEST'); + $query['baseok'] = true; + $query['filter'] = get_request('filter','REQUEST',false,'objectclass=*'); + $query['scope'] = get_request('scope','REQUEST',false,'base'); + $query['deref'] = $_SESSION[APPCONFIG]->getValue('deref','export'); + $query['size_limit'] = 0; + $attrs = get_request('attributes','REQUEST'); + + $attrs = preg_replace('/\s+/','',$attrs); + if ($attrs) + $query['attrs'] = explode(',',$attrs); + else + $query['attrs'] = array('*'); + + if (get_request('sys_attr')) { + if (! in_array('*',$query['attrs'])) + array_push($query['attrs'],'*'); + array_push($query['attrs'],'+'); + } + + if (! $base) + $bases = $server->getBaseDN(); + else + $bases = array($base); + + foreach ($bases as $base) { + $query['base'] = $base; + + $time_start = utime(); + $this->results[$base] = $server->query($query,null); + $time_end = utime(); + + usort($this->results[$base],'pla_compare_dns'); + $this->resultsdata[$base]['time'] = round($time_end-$time_start,2); + + # If no result, there is a something wrong + if (! $this->results[$base] && $server->getErrorNum(null)) + system_message(array( + 'title'=>_('Encountered an error while performing search.'), + 'body'=>ldap_error_msg($server->getErrorMessage(null),$server->getErrorNum(null)), + 'type'=>'error')); + + $this->items += count($this->results[$base]); + } + + $this->resultsdata['scope'] = $query['scope']; + $this->resultsdata['filter'] = $query['filter']; + $this->resultsdata['attrs'] = $query['attrs']; + + # Other settings + switch (get_request('format','POST',false,'unix')) { + case 'win': + $this->br = "\r\n"; + break; + + case 'mac': + $this->br = "\r"; + break; + + case 'unix': + default: + $this->br = "\n"; + } + + if (get_request('compress','REQUEST') == 'on') + $this->compress = true; + } + + public function isCompressed() { + return $this->compress; + } + + protected function getHeader() { + $server = $this->getServer(); + $type = $this->getType(); + + $output = ''; + + $output .= sprintf('# %s %s %s%s',$type['description'],_('for'),implode('|',array_keys($this->results)),$this->br); + $output .= sprintf('# %s: %s (%s)%s',_('Server'),$server->getName(),$server->getValue('server','host'),$this->br); + $output .= sprintf('# %s: %s%s',_('Search Scope'),$this->resultsdata['scope'],$this->br); + $output .= sprintf('# %s: %s%s',_('Search Filter'),$this->resultsdata['filter'],$this->br); + $output .= sprintf('# %s: %s%s',_('Total Entries'),$this->items,$this->br); + $output .= sprintf('#%s',$this->br); + $output .= sprintf('# Generated by %s (%s) on %s%s',app_name(),get_href('web'),date('F j, Y g:i a'),$this->br); + $output .= sprintf('# Version: %s%s',app_version(),$this->br); + + $output .= $this->br; + + return $output; + } + + /** + * Helper method to check if the attribute value should be base 64 encoded. + * + * @param The string to check. + * @return boolean true if the string is safe ascii, false otherwise. + */ + protected function isSafeAscii($str) { + for ($i=0;$i 127) + return false; + + return true; + } +} + +/** + * Export entries to CSV + * + * @package phpLDAPadmin + * @subpackage Export + */ +class ExportCSV extends Export { + private $separator = ','; + private $qualifier = '"'; + private $multivalue_separator = ' | '; + private $escapeCode = '"'; + + static public function getType() { + return array('type'=>'CSV','description' => 'CSV (Spreadsheet)','extension'=>'csv'); + } + + function export() { + $server = $this->getServer(); + + /* Go thru and find all the attribute names first. This is needed, because, otherwise we have + * no idea as to which search attributes were actually populated with data */ + $headers = array('dn'); + $entries = array(); + foreach ($this->results as $base => $results) { + foreach ($results as $dndetails) { + array_push($entries,$dndetails); + + unset($dndetails['dn']); + foreach (array_keys($dndetails) as $key) + if (! in_array($key,$headers)) + array_push($headers,$key); + + } + } + + $output = ''; + $num_headers = count($headers); + + # Print out the headers + for ($i=0; $i<$num_headers; $i++) { + $output .= sprintf('%s%s%s',$this->qualifier,$headers[$i],$this->qualifier); + + if ($i < $num_headers-1) + $output .= $this->separator; + } + + # Drop out our DN header. + array_shift($headers); + $num_headers--; + + $output .= $this->br; + + # Loop on every entry + foreach ($entries as $index => $entry) { + $dn = $entry['dn']; + unset($entry['dn']); + $output .= sprintf('%s%s%s%s',$this->qualifier,$this->LdapEscape($dn),$this->qualifier,$this->separator); + + # Print the attributes + for ($j=0; $j<$num_headers; $j++) { + $attr = $headers[$j]; + $output .= $this->qualifier; + + if (array_key_exists($attr,$entry)) { + $binary_attribute = $server->isAttrBinary($attr) ? 1 : 0; + + if (! is_array($entry[$attr])) + $attr_values = array($entry[$attr]); + else + $attr_values = $entry[$attr]; + + $num_attr_values = count($attr_values); + + for ($i=0; $i<$num_attr_values; $i++) { + if ($binary_attribute) + $output .= base64_encode($attr_values[$i]); + else + $output .= $this->LdapEscape($attr_values[$i]); + + if ($i < $num_attr_values-1) + $output .= $this->multivalue_separator; + } + } + + $output .= $this->qualifier; + + if ($j < $num_headers-1) + $output .= $this->separator; + } + + $output .= $this->br; + } + + if ($this->compress) + echo gzencode($output); + else + echo $output; + } + + /** + * Function to escape data, where the qualifier happens to also + * be in the data. + */ + private function LdapEscape ($var) { + return str_replace($this->qualifier,$this->escapeCode.$this->qualifier,$var); + } +} + +/** + * Export entries to DSML v.1 + * + * @package phpLDAPadmin + * @subpackage Export + */ +class ExportDSML extends Export { + static public function getType() { + return array('type'=>'DSML','description' => _('DSML V.1 Export'),'extension'=>'xml'); + } + + /** + * Export entries to DSML format + */ + function export() { + $server = $this->getServer(); + + # Not very elegant, but do the job for the moment as we have just 4 level + $indent = array(); + $indent['dir'] = ' '; + $indent['ent'] = ' '; + $indent['att'] = ' '; + $indent['val'] = ' '; + + # Print declaration + $output = sprintf('%s',$this->br); + + # Print root element + $output .= sprintf('%s',$this->br); + + # Print info related to this export + $output .= sprintf('%s',$this->br); + $output .= $this->br; + + $output .= sprintf('%s%s',$indent['dir'],$this->br); + + # Sift through the entries. + $counter = 0; + foreach ($this->results as $base => $results) { + foreach ($results as $dndetails) { + $counter++; + + $dn = $dndetails['dn']; + unset($dndetails['dn']); + ksort($dndetails); + + # Display DN + $output .= sprintf('%s%s',$indent['ent'],htmlspecialchars($dn),$this->br); + + # Display the objectClass attributes first + if (isset($dndetails['objectClass'])) { + if (! is_array($dndetails['objectClass'])) + $dndetails['objectClass'] = array($dndetails['objectClass']); + + $output .= sprintf('%s%s',$indent['att'],$this->br); + + foreach ($dndetails['objectClass'] as $ocValue) + $output .= sprintf('%s%s%s',$indent['val'],$ocValue,$this->br); + + $output .= sprintf('%s%s',$indent['att'],$this->br); + unset($dndetails['objectClass']); + } + + # Display the attributes + foreach ($dndetails as $key => $attr) { + if (! is_array($attr)) + $attr = array($attr); + + $output .= sprintf('%s%s',$indent['att'],$key,$this->br); + + # If the attribute is binary, set the flag $binary_mode to true + $binary_mode = $server->isAttrBinary($key) ? 1 : 0; + + foreach ($attr as $value) + $output .= sprintf('%s%s%s', + $indent['val'],($binary_mode ? base64_encode($value) : htmlspecialchars($value)),$this->br); + + $output .= sprintf('%s%s',$indent['att'],$this->br); + } + + $output .= sprintf('%s%s',$indent['ent'],$this->br); + } + } + + $output .= sprintf('%s%s',$indent['dir'],$this->br); + $output .= sprintf('%s',$this->br); + + if ($this->compress) + echo gzencode($output); + else + echo $output; + } +} + +/** + * Export from LDAP using an LDIF format + * + * @package phpLDAPadmin + * @subpackage Export + */ +class ExportLDIF extends Export { + # The maximum length of the ldif line + private $line_length = 76; + + static public function getType() { + return array('type'=>'LDIF','description' => _('LDIF Export'),'extension'=>'ldif'); + } + + /** + * Export entries to LDIF format + */ + public function export() { + if (! $this->results) { + echo _('Nothing to export'); + return; + } + + $server = $this->getServer(); + + $output = $this->getHeader(); + + # Add our version. + $output .= 'version: 1'; + $output .= $this->br; + $output .= $this->br; + + # Sift through the entries. + $counter = 0; + foreach ($this->results as $base => $results) { + foreach ($results as $dndetails) { + $counter++; + + $dn = $dndetails['dn']; + unset($dndetails['dn']); + ksort($dndetails); + + $title_string = sprintf('# %s %s: %s%s',_('Entry'),$counter,$dn,$this->br); + + if (strlen($title_string) > $this->line_length-3) + $title_string = substr($title_string,0,$this->line_length-3).'...'.$this->br; + + $output .= $title_string; + + # Display dn + if ($this->isSafeAscii($dn)) + $output .= $this->multiLineDisplay(sprintf('dn: %s',$dn)); + else + $output .= $this->multiLineDisplay(sprintf('dn:: %s',base64_encode($dn))); + + # display the attributes + foreach ($dndetails as $key => $attr) { + if (! is_array($attr)) + $attr = array($attr); + + foreach ($attr as $value) + if (! $this->isSafeAscii($value) || $server->isAttrBinary($key)) + $output .= $this->multiLineDisplay(sprintf('%s:: %s',$key,base64_encode($value))); + else + $output .= $this->multiLineDisplay(sprintf('%s: %s',$key,$value)); + } + + $output .= $this->br; + } + } + + if ($this->compress) + echo gzencode($output); + else + echo $output; + } + + /** + * Helper method to wrap ldif lines + * + * @param The line to be wrapped if needed. + */ + private function multiLineDisplay($str) { + $length_string = strlen($str); + $length_max = $this->line_length; + + $output = ''; + while ($length_string > $length_max) { + $output .= substr($str,0,$length_max).$this->br.' '; + $str = substr($str,$length_max,$length_string); + $length_string = strlen($str); + + /* Need to do minus one to align on the right + * the first line with the possible following lines + * as these will have an extra space. */ + $length_max = $this->line_length-1; + } + + $output .= $str.$this->br; + + return $output; + } +} + +/** + * Export entries to VCARD v2.1 + * + * @package phpLDAPadmin + * @subpackage Export + */ +class ExportVCARD extends Export { + static public function getType() { + return array('type'=>'VCARD','description' => _('VCARD 2.1 Export'),'extension'=>'vcf'); + } + + # Mappping one to one attribute + private $mapping = array( + 'cn' => 'FN', + 'title' => 'TITLE', + 'homephone' => 'TEL;HOME', + 'mobile' => 'TEL;CELL', + 'mail' => 'EMAIL;Internet', + 'labeleduri' =>'URL', + 'o' => 'ORG', + 'audio' => 'SOUND', + 'facsmiletelephoneNumber' =>'TEL;WORK;HOME;VOICE;FAX', + 'jpegphoto' => 'PHOTO;ENCODING=BASE64', + 'businesscategory' => 'ROLE', + 'description' => 'NOTE' + ); + + private $deliveryAddress = array( + 'postofficebox', + 'street', + 'l', + 'st', + 'postalcode', + 'c'); + + /** + * Export entries to VCARD format + */ + function export() { + $server = $this->getServer(); + + # Sift through the entries. + foreach ($this->results as $base => $results) { + foreach ($results as $dndetails) { + $dndetails = array_change_key_case($dndetails); + + # Check the attributes needed for the delivery address field + $addr = 'ADR:'; + foreach ($this->deliveryAddress as $attr) { + if (isset($dndetails[$attr])) { + $addr .= $dndetails[$attr]; + unset($dndetails[$attr]); + } + $addr .= ';'; + } + + $output = sprintf('BEGIN:VCARD%s',$this->br); + + # Loop for the attributes + foreach ($dndetails as $key => $attr) { + if (! is_array($attr)) + $attr = array($attr); + + # If an attribute of the ldap entry exist in the mapping array for vcard + if (isset($this->mapping[$key])) { + + # Case of organisation. Need to append the possible ou attribute + if ($key == 'o') { + $output .= sprintf('%s:%s',$this->mapping[$key],$attr[0]); + + if (isset($entry['ou'])) + foreach ($entry['ou'] as $ou_value) + $output .= sprintf(';%s',$ou_value); + + # The attribute is binary. (to do : need to fold the line) + } elseif (in_array($key,array('audio','jpegphoto'))) { + $output .= $this->mapping[$key].':'.$this->br; + $output .= ' '.base64_encode($attr[0]); + + } else { + $output .= $this->mapping[$key].':'.$attr[0]; + } + + $output .= $this->br; + } + } + + $output .= sprintf('UID:%s%s',isset($dndetails['entryUUID']) ? $dndetails['entryUUID'] : $dndetails['dn'],$this->br); + $output .= sprintf('VERSION:2.1%s',$this->br); + $output .= sprintf('%s%s',$addr,$this->br); + $output .= sprintf('END:VCARD%s',$this->br); + } + } + + if ($this->compress) + echo gzencode($output); + else + echo $output; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/functions.php b/lam/templates/3rdParty/pla/lib/functions.php new file mode 100644 index 00000000..47346935 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/functions.php @@ -0,0 +1,3076 @@ +_('Generic Error'), + 'body'=>sprintf('%s: %s [%s]', + __METHOD__,_('Called to load a class that cant be found'),$className), + 'type'=>'error')); +} + +/** + * Strips all slashes from the specified array in place (pass by ref). + * @param Array The array to strip slashes from, typically one of + * $_GET, $_POST, or $_COOKIE. + */ +function array_stripslashes(&$array) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (is_array($array)) + while (list($key) = each($array)) + if (is_array($array[$key]) && $key != $array) + array_stripslashes($array[$key]); + else + $array[$key] = stripslashes($array[$key]); +} + +/** + * Compatibility Functions + * These functions exist, so that a standard function can be used in new applications, and they + * map to already defined functions in older applications. + */ + +/** + * If gettext is not available in PHP, then this will provide compatibility for it. + */ +if (! function_exists('_')) { + function _($msg) { + return $msg; + } +} + +/** + * Generic Utility Functions + */ + +/** + * Custom error handling function. + * When a PHP error occurs, PHP will call this function rather than printing + * the typical PHP error string. This provides the application the ability to + * format an error message so that it looks better. + * Optionally, it can present a link so that a user can search/submit bugs. + * This function is not to be called directly. It is exclusively for the use of + * PHP internally. If this function is called by PHP from within a context + * where error handling has been disabled (ie, from within a function called + * with "@" prepended), then this function does nothing. + * + * @param int The PHP error number that occurred (ie, E_ERROR, E_WARNING, E_PARSE, etc). + * @param string The PHP error string provided (ie, "Warning index "foo" is undefined) + * @param string The file in which the PHP error ocurred. + * @param int The line number on which the PHP error ocurred + * @see set_error_handler + */ +function app_error_handler($errno,$errstr,$file,$lineno) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + /** + * error_reporting will be 0 if the error context occurred + * within a function call with '@' preprended (ie, @ldap_bind() ); + * So, don't report errors if the caller has specifically + * disabled them with '@' + */ + if (ini_get('error_reporting') == 0 || error_reporting() == 0) + return; + + $file = basename($file); + $caller = basename($_SERVER['PHP_SELF']); + $errtype = ''; + + switch ($errno) { + case E_STRICT: $errtype = 'E_STRICT'; break; + case E_ERROR: $errtype = 'E_ERROR'; break; + case E_WARNING: $errtype = 'E_WARNING'; break; + case E_PARSE: $errtype = 'E_PARSE'; break; + case E_NOTICE: $errtype = 'E_NOTICE'; break; + case E_CORE_ERROR: $errtype = 'E_CORE_ERROR'; break; + case E_CORE_WARNING: $errtype = 'E_CORE_WARNING'; break; + case E_COMPILE_ERROR: $errtype = 'E_COMPILE_ERROR'; break; + case E_COMPILE_WARNING: $errtype = 'E_COMPILE_WARNING'; break; + case E_USER_ERROR: $errtype = 'E_USER_ERROR'; break; + case E_USER_WARNING: $errtype = 'E_USER_WARNING'; break; + case E_USER_NOTICE: $errtype = 'E_USER_NOTICE'; break; + case E_ALL: $errtype = 'E_ALL'; break; + + default: $errtype = sprintf('%s: %s',_('Unrecognized error number'),$errno); + } + + # Take out extra spaces in error strings. + $errstr = preg_replace('/\s+/',' ',$errstr); + + if ($errno == E_NOTICE) { + $body = ''; + $body .= sprintf('',_('Error'),$errstr,$errtype); + $body .= sprintf('', + _('File'),$file,_('line'),$lineno,_('caller'),$caller); + $body .= sprintf('', + app_version(),phpversion(),php_sapi_name()); + $body .= sprintf('',isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : 'SCRIPT'); + + if (function_exists('get_href')) + $body .= sprintf('', + get_href('search_bug',"&summary_keyword=".rawurlencode($errstr)), + _('Please check and see if this bug has been reported')); + $body .= '
%s:%s (%s)
%s:%s %s %s, %s %s
Versions:PLA: %s, PHP: %s, SAPI: %s
Web server:%s
%s.
'; + + system_message(array( + 'title'=>_('You found a non-fatal phpLDAPadmin bug!'), + 'body'=>$body, + 'type'=>'error')); + + return; + } + + # If this is a more serious error, call the error call. + error(sprintf('%s: %s',$errtype,$errstr),'error',null,true,true); +} + +/** + * Returns the application name. + */ +function app_name() { + return 'phpLDAPadmin'; +} + +/** + * Returns the application version currently running. The version + * is read from the file named VERSION. + * + * @return string The current version as read from the VERSION file. + */ +function app_version() { + static $CACHE = null; + + if ($CACHE) + return $CACHE; + + $version_file = realpath(LIBDIR.'../VERSION'); + if (! file_exists($version_file)) + $CACHE = 'UNKNOWN'; + + else { + $version = rtrim(file_get_contents($version_file)); + + $CACHE = preg_replace('/^RELEASE-([0-9\.]+(-.*)*)$/','$1',$version); + + # Check if we are a CVS copy. + if (preg_match('/^(DEVEL)?$/',$CACHE)) + $CACHE = 'DEVEL'; + + # Check if we are special DEVEL version + elseif (preg_match('/^DEVEL-([0-9\.]+)+$/',$CACHE)) {} + + # If return is still the same as version, then the tag is not one we expect. + elseif ($CACHE == $version) + $CACHE = 'UNKNOWN'; + } + + return $CACHE; +} + +/** + * This function will convert the browser two character language into the + * default 5 character language, where the country portion should NOT be + * assumed to be upper case characters of the first two characters. + */ +function auto_lang($lang) { + switch ($lang) { + case 'ja': return 'ja_JP'; + case 'cs': return 'cs_CZ'; + default: return sprintf('%s_%s',$lang,strtoupper($lang)); + } +} + +/** + * Makes sure that the config file is properly setup. + */ +function check_config($config_file) { + # Read in config_default.php + require_once LIBDIR.'config_default.php'; + + # Make sure their PHP version is current enough + if (strcmp(phpversion(),REQUIRED_PHP_VERSION) < 0) + system_message(array( + 'title'=>_('Incorrect version of PHP'), + 'body'=>sprintf('phpLDAPadmin requires PHP version %s or greater.
(You are using %s)', + REQUIRED_PHP_VERSION,phpversion()), + 'type'=>'error')); + + $config = new Config; + + if (file_exists(LIBDIR.'config_custom.php') && is_readable(LIBDIR.'config_custom.php')) + include LIBDIR.'config_custom.php'; + + ob_start(); + require $config_file; + $str = ''; + if (ob_get_level()) { + $str = ob_get_contents(); + ob_end_clean(); + } + + if ($str) { + $str = strip_tags($str); + $matches = array(); + preg_match('/(.*):\s+(.*):.*\s+on line (\d+)/',$str,$matches); + + if (isset($matches[1]) && isset($matches[2]) && isset($matches[3])) { + $error_type = $matches[1]; + $error = $matches[2]; + $line_num = $matches[3]; + + $file = file($config_file); + + $body = '

Config file ERROR

'; + $body .= sprintf('

%s (%s) on line %s

',$error_type,$error,$line_num); + + $body .= '
'; + $body .= sprintf('Looks like your config file has an ERROR on line %s.
',$line_num); + $body .= 'Here is a snippet around that line
'; + $body .= '
'."\n"; + + $body .= '
'; + + for ($i = $line_num-9; $i<$line_num+5; $i++) { + if ($i+1 == $line_num) + $body .= '
'; + + if ($i < 0) + continue; + + $body .= sprintf('%s: %s
',$i+1,$file[$i]); + + if ($i+1 == $line_num) + $body .= '
'; + } + + $body .= '
'; + $body .= '
'; + $body .= 'Hint: Sometimes these errors are caused by lines preceding the line reported.'; + $body .= '
'; + + $block = new block(); + $block->SetBody($body); + $www['page'] = new page(); + $www['page']->block_add('body',$block); + $www['page']->display(); + + die(); + } + } + + # Check for server definitions. + if (! isset($servers) || count($servers->GetServerList()) == 0) + error(_('Your config.php is missing Server Definitions. Please see the sample file config/config.php.example.'),'error','index.php',true); + + $config->setServers($servers); + + # Check the memory limit parameter. + if ((ini_get('memory_limit') > -1) && ini_get('memory_limit') < $config->getValue('session','memorylimit')) + system_message(array( + 'title'=>_('Memory Limit low.'), + 'body'=>sprintf('Your php memory limit is low - currently %s, you should increase it to atleast %s. This is normally controlled in /etc/php.ini.', + ini_get('memory_limit'),$config->getValue('session','memorylimit')), + 'type'=>'error')); + + return $config; +} + +/** + * Commands available in the control_panel of the page + * + * @return array + */ +function cmd_control_pane($type) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + switch ($type) { + case 'main' : + return array( + 'home'=>array( + 'title'=>_('Home'), + 'enable'=>false, + 'link'=>sprintf('href="index.php" title="%s"',_('Home')), + 'image'=>sprintf('%s',IMGDIR,_('Home'))), + + 'purge'=>array( + 'title'=>_('Purge caches'), + 'enable'=>isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->isCommandAvailable('script','purge_cache') : false, + 'link'=>sprintf('href="cmd.php?cmd=purge_cache" onclick="return ajDISPLAY(\'BODY\',\'cmd=purge_cache\',\'%s\');" title="%s"', + _('Clearing cache'),_('Purge caches')), + 'image'=>sprintf('%s',IMGDIR,_('Purge caches'))), + + 'hide_debug_info'=>array( + 'title'=>_('Show Cache'), + 'enable'=>isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->isCommandAvailable('script','show_cache') : false, + 'link'=>sprintf('href="cmd.php?cmd=show_cache" onclick="return ajDISPLAY(\'BODY\',\'cmd=show_cache\',\'%s\');" title="%s"', + _('Loading'),_('Show Cache'),_('Show Cache')), + 'image'=>sprintf('%s',IMGDIR,_('Show Cache'))), + ); + + break; + + case 'top' : + return array( + 'forum'=>array( + 'title'=>_('Forum'), + 'enable'=>isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->isCommandAvailable('cmd','oslinks') : true, + 'link'=>sprintf('href="%s" title="%s" onclick="target=\'_blank\';"',get_href('forum'),_('Forum')), + 'image'=>sprintf('%s',IMGDIR,_('Forum'))), + + 'feature'=>array( + 'title'=>_('Request feature'), + 'enable'=>isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->isCommandAvailable('cmd','oslinks') : true, + 'link'=>sprintf('href="%s" title="%s" onclick="target=\'_blank\';"',get_href('add_rfe'),_('Request feature')), + 'image'=>sprintf('%s',IMGDIR,_('Request feature'))), + + 'bug'=>array( + 'title'=>_('Report a bug'), + 'enable'=>isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->isCommandAvailable('cmd','oslinks') : true, + 'link'=>sprintf('href="%s" title="%s" onclick="target=\'_blank\';"',get_href('add_bug'),_('Report a bug')), + 'image'=>sprintf('%s',IMGDIR,_('Report a bug'))), + + 'donation'=>array( + 'title'=>_('Donate'), + 'enable'=>isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->isCommandAvailable('cmd','oslinks') : true, + 'link'=>sprintf('href="%s" title="%s" onclick="target=\'_blank\';"',get_href('donate'),_('Donate')), + 'image'=>sprintf('%s',IMGDIR,_('Donate'))), + + 'help'=>array( + 'title'=>_('Help'), + 'enable'=>isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->isCommandAvailable('cmd','oslinks') : true, + 'link'=>sprintf('href="%s" title="%s" onclick="target=\'_blank\';"',get_href('documentation'),_('Help')), + 'image'=>sprintf('%s',IMGDIR,_('Help'))) + ); + + break; + } +} + +/** + * This function dumps the $variable for debugging purposes + * + * @param string|array Variable to dump + * @param boolean Whether to stop execution or not. + */ +function debug_dump($variable,$die=false,$onlydebugaddr=false) { + if ($onlydebugaddr && + isset($_SESSION[APPCONFIG]) && $_SESSION[APPCONFIG]->getValue('debug','addr') && + $_SERVER['HTTP_X_FORWARDED_FOR'] != $_SESSION[APPCONFIG]->getValue('debug','addr') && + $_SERVER['REMOTE_ADDR'] != $_SESSION[APPCONFIG]->getValue('debug','addr')) + return; + + $backtrace = debug_backtrace(); + $caller['class'] = isset($backtrace[0]['class']) ? $backtrace[0]['class'] : 'N/A'; + $caller['function'] = isset($backtrace[0]['function']) ? $backtrace[0]['function'] : 'N/A'; + $caller['file'] = isset($backtrace[0]['file']) ? $backtrace[0]['file'] : 'N/A'; + $caller['line'] = isset($backtrace[0]['line']) ? $backtrace[0]['line'] : 'N/A'; + $caller['debug'] = $variable; + + print '
';
+	print_r($caller);
+	print '
'; + + if ($die) + die(); +} + +/** + * This function generates a backtrace + * + * @param boolean Whether to stop execution or not. + */ +function debug_dump_backtrace($msg='Calling BackTrace',$die=false) { + error($msg,'note',null,$die,true); +} + +/** + * Send a debug as a sys message + */ +function debug_sysmsg($msg) { + system_message(array('title'=>_('Debug'),'body'=>$msg,'type'=>'debug')); +} + +/** + * Debug Logging + * + * The global debug level is turned on in your configuration file by setting: + * + * $config->custom->debug['level'] = 255; + * + * together with atleast one output direction (currently file and syslog are supported). + * + * $config->custom->debug['file'] = '/tmp/app_debug.log'; + * $config->custom->debug['syslog'] = true; + * + * + * The debug level is turned into binary, then if the message levels bit is on + * the message will be sent to the debug log. (Thus setting your debug level to 255, + * all bits on, will results in all messages being printed.) + * + * The message level bits are defined here. + * 0( 1) = Entry/Return results from function calls. + * 1( 2) = Configuration Processing + * 2( 4) = Template Processing + * 3( 8) = Schema Processing + * 4( 16) = LDAP Server Communication + * 5( 32) = Tree Processing + * 7( 64) = Other non generic messages + * 8(128) = Page Processing + * 9(256) = Hooks Processing + * @param string Message to send to syslog + * @param int Log bit number for this message. + * @see syslog.php + */ +function debug_log($msg,$level,$indent) { + static $debug_file; + + # In case we are called before we are fully initialised or if debugging is not set. + if (! isset($_SESSION[APPCONFIG]) + || ! ($_SESSION[APPCONFIG]->getValue('debug','file') || $_SESSION[APPCONFIG]->getValue('debug','syslog'))) + return; + + $debug_level = $_SESSION[APPCONFIG]->getValue('debug','level'); + if (! $debug_level || (! ($level & $debug_level))) + return; + + if ($_SESSION[APPCONFIG]->getValue('debug','addr')) + if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] == $_SESSION[APPCONFIG]->getValue('debug','addr')) + $debugaddr = true; + elseif ($_SERVER['REMOTE_ADDR'] == $_SESSION[APPCONFIG]->getValue('debug','addr')) + $debugaddr = true; + else + $debugaddr = false; + + else + $debugaddr = true; + + if (! $debugaddr) + return; + + # If we are limiting debug to a browser, then check that + $caller = basename($_SERVER['PHP_SELF']); + + $args = func_get_args(); + # Discard our first three arguments. + array_shift($args); + array_shift($args); + array_shift($args); + + # Pull the file/line/method + if (is_string($args[0]) && preg_match('/.php$/',$args[0])) { + $file = preg_replace('/.php$/','',array_shift($args)); + $line = array_shift($args); + $method = array_shift($args); + + } else { + $file = 'UNKNOWN'; + $line = 'UNKNOWN'; + $method = 'UNKNOWN'; + } + + # TEMP: New debuglog format + if (preg_match('/%%/',$msg) && $args[0] != 'NOARGS') + $args = array_shift($args); + + $fargs = array(); + foreach ($args as $key) { + if (is_array($key)) + array_push($fargs,serialize($key)); + elseif (is_object($key)) + array_push($fargs,sprintf('OBJECT:%s',get_class($key))); + else + array_push($fargs,$key); + } + + if (preg_match('/%%/',$msg)) + $msg = preg_replace('/%%/',join('|',$fargs),$msg); + else + $msg = vsprintf($msg,array_values($fargs)); + + if (function_exists('stopwatch')) + $timer = stopwatch(); + else + $timer = null; + + $debug_message = sprintf('[%2.3f] %15s(%04s-%03s): %s%s: %s',$timer,basename($file),$line,$level,str_repeat('.',$indent),$method,substr($msg,0,200)); + + if ($debug_file || $_SESSION[APPCONFIG]->getValue('debug','file')) { + if (! $debug_file) + $debug_file = fopen($_SESSION[APPCONFIG]->getValue('debug','file'), + $_SESSION[APPCONFIG]->getValue('debug','append') ? 'a' : 'w'); + + fwrite($debug_file,$debug_message."\n"); + } + + if ($_SESSION[APPCONFIG]->getValue('debug','syslog') && function_exists('syslog_notice')) + syslog_notice($debug_message); +} + +/** + * Display an error message in the system message panel of the page. + */ +function error($msg,$type='note',$redirect=null,$fatal=false,$backtrace=false) { + global $www; + static $counter; + + # Just a check to see that we are called right. + if (! isset($www['page']) && ! $fatal) + die("Function error called incorrectly [$msg]"); + + # If the error is fatal, we'll need to stop here. + if (! isset($www['page'])) + $www['page'] = new page(); + + if ($fatal) + $www['page']->setsysmsg(array('title'=>_('Error'),'body'=>$msg,'type'=>$type)); + else + system_message(array('title'=>_('Error'),'body'=>$msg,'type'=>$type),$redirect); + + # Spin loop detection + if ($counter++ > 20) { + debug_dump('Spin loop detection.'); + debug_dump(array('msg'=>$msg,'session'=>$_SESSION['sysmsg'],'www'=>$www),1); + } + + # Do we have a backtrace to display? + if ($backtrace) { + $backtraceblock = new block(); + $backtraceblock->SetTitle('PHP Debug Backtrace'); + + $body = ''; + $body .= "\n"; + + foreach (debug_backtrace() as $error => $line) { + $_SESSION['backtrace'][$error]['file'] = isset($line['file']) ? $line['file'] : 'unknown'; + $_SESSION['backtrace'][$error]['line'] = isset($line['line']) ? $line['line'] : 'unknown'; + $body .= sprintf('', + _('File'),isset($line['file']) ? $line['file'] : $last['file'],isset($line['line']) ? $line['line'] : ''); + + $_SESSION['backtrace'][$error]['function'] = $line['function']; + $body .= sprintf(''; + $body .= "\n"; + + if (isset($line['file'])) + $last['file'] = $line['file']; + } + + $body .= '
%s%s (%s)
 %s%s', + _('Function'),$line['function']); + + if (isset($line['args'])) { + $display = strlen(serialize($line['args'])) < 50 ? htmlspecialchars(serialize($line['args'])) : htmlspecialchars(substr(serialize($line['args']),0,50)).'...'; + $_SESSION['backtrace'][$error]['args'] = $line['args']; + if (file_exists(LIBDIR.'../tools/unserialize.php')) + $body .= sprintf(' (%s)', + '../tools/unserialize.php',$error,$display); + else + $body .= sprintf(' (%s)',$display); + } + $body .= '
'; + $body .= "\n"; + $backtraceblock->SetBody($body); + + $www['page']->block_add('body',$backtraceblock); + } + + if ($fatal) { + $www['page']->display(array('tree'=>false)); + die(); + } +} + +/** + * Return the result of a form variable, with optional default + * + * @return The form GET/REQUEST/SESSION/POST variable value or its default + */ +function get_request($attr,$type='POST',$die=false,$default=null) { + switch($type) { + case 'GET': + $value = isset($_GET[$attr]) ? (is_array($_GET[$attr]) ? $_GET[$attr] : (trim(empty($_GET['nodecode'][$attr]) ? rawurldecode($_GET[$attr]) : $_GET[$attr]))) : $default; + break; + + case 'REQUEST': + $value = isset($_REQUEST[$attr]) ? (is_array($_REQUEST[$attr]) ? $_REQUEST[$attr] : trim(empty($_REQUEST['nodecode'][$attr]) ? rawurldecode($_REQUEST[$attr]) : $_REQUEST[$attr])) : $default; + break; + + case 'SESSION': + $value = isset($_SESSION[$attr]) ? (is_array($_SESSION[$attr]) ? $_SESSION[$attr] : (empty($_SESSION['nodecode'][$attr]) ? rawurldecode($_SESSION[$attr]) : $_SESSION[$attr])) : $default; + break; + + case 'POST': + default: + $value = isset($_POST[$attr]) ? (is_array($_POST[$attr]) ? $_POST[$attr] : trim(empty($_POST['nodecode'][$attr]) ? rawurldecode($_POST[$attr]) : $_POST[$attr])) : $default; + break; + } + + if ($die && is_null($value)) + system_message(array( + 'title'=>_('Generic Error'), + 'body'=>sprintf('%s: Called "%s" without "%s" using "%s"', + basename($_SERVER['PHP_SELF']),get_request('cmd','REQUEST'),$attr,$type), + 'type'=>'error'), + 'index.php'); + + return $value; +} + +/** + * Record a system message. + * This function can be used as an alternative to generate a system message, if page hasnt yet been defined. + */ +function system_message($msg,$redirect=null) { + if (! is_array($msg)) + return null; + + if (! isset($msg['title']) && ! isset($msg['body'])) + return null; + + if (! isset($msg['type'])) + $msg['type'] = 'info'; + + if (! isset($_SESSION['sysmsg']) || ! is_array($_SESSION['sysmsg'])) + $_SESSION['sysmsg'] = array(); + + # Try and detect if we are in a redirect loop + if (get_request('redirect','GET') && $msg['type'] != 'debug') { + foreach ($_SESSION['sysmsg'] as $detail) { + if ($msg == $detail && ! isset($detail['special'])) { + debug_dump(array('Incoming MSG'=>$msg,'existing'=>$_SESSION['sysmsg'])); + debug_dump_backtrace('Redirect Loop Detected',true); + } + } + } + + array_push($_SESSION['sysmsg'],$msg); + + if ($redirect) { + if (preg_match('/\?/',$redirect)) + $redirect .= '&'; + else + $redirect .= '?'; + $redirect .= 'redirect=true'; + + # Check if we were an ajax request, and only render the ajax message + if (get_request('meth','REQUEST') == 'ajax') + $redirect .= '&meth=ajax'; + + header("Location: $redirect"); + die(); + } +} + +/** + * Other Functions + */ + +/** + * Encryption using blowfish algorithm + * + * @param string Original data + * @param string The secret + * @return string The encrypted result + * @author lem9 (taken from the phpMyAdmin source) + */ +function blowfish_encrypt($data,$secret=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # If our secret is null or blank, get the default. + if ($secret === null || ! trim($secret)) + $secret = $_SESSION[APPCONFIG]->getValue('session','blowfish') ? $_SESSION[APPCONFIG]->getValue('session','blowfish') : session_id(); + + # If the secret isnt set, then just return the data. + if (! trim($secret)) + return $data; + + if (function_exists('mcrypt_module_open')) { + $td = mcrypt_module_open(MCRYPT_BLOWFISH,'',MCRYPT_MODE_ECB,''); + $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td),MCRYPT_DEV_URANDOM); + mcrypt_generic_init($td,substr($secret,0,mcrypt_enc_get_key_size($td)),$iv); + $encrypted_data = base64_encode(mcrypt_generic($td,$data)); + mcrypt_generic_deinit($td); + + return $encrypted_data; + } + + if (file_exists(LIBDIR.'blowfish.php')) + require_once LIBDIR.'blowfish.php'; + else + return $data; + + $pma_cipher = new Horde_Cipher_blowfish; + $encrypt = ''; + + for ($i=0; $iencryptBlock($block, $secret); + } + return base64_encode($encrypt); +} + +/** + * Decryption using blowfish algorithm + * + * @param string Encrypted data + * @param string The secret + * @return string Original data + * @author lem9 (taken from the phpMyAdmin source) + */ +function blowfish_decrypt($encdata,$secret=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # This cache gives major speed up for stupid callers :) + static $CACHE = array(); + + if (isset($CACHE[$encdata])) + return $CACHE[$encdata]; + + # If our secret is null or blank, get the default. + if ($secret === null || ! trim($secret)) + $secret = $_SESSION[APPCONFIG]->getValue('session','blowfish') ? $_SESSION[APPCONFIG]->getValue('session','blowfish') : session_id(); + + # If the secret isnt set, then just return the data. + if (! trim($secret)) + return $encdata; + + if (function_exists('mcrypt_module_open')) { + $td = mcrypt_module_open(MCRYPT_BLOWFISH,'',MCRYPT_MODE_ECB,''); + $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td),MCRYPT_DEV_URANDOM); + mcrypt_generic_init($td,substr($secret,0,mcrypt_enc_get_key_size($td)),$iv); + $decrypted_data = trim(mdecrypt_generic($td,base64_decode($encdata))); + mcrypt_generic_deinit($td); + + return $decrypted_data; + } + + if (file_exists(LIBDIR.'blowfish.php')) + require_once LIBDIR.'blowfish.php'; + else + return $encdata; + + $pma_cipher = new Horde_Cipher_blowfish; + $decrypt = ''; + $data = base64_decode($encdata); + + for ($i=0; $idecryptBlock(substr($data, $i, 8), $secret); + + $return = trim($decrypt); + $CACHE[$encdata] = $return; + return $return; +} + +/** + * String padding + * + * @param string Input string + * @param integer Length of the result + * @param string The filling string + * @param integer Padding mode + * @return string The padded string + */ +function full_str_pad($input,$pad_length,$pad_string='',$pad_type=0) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $str = ''; + $length = $pad_length - strlen($input); + + if ($length > 0) { // str_repeat doesn't like negatives + if ($pad_type == STR_PAD_RIGHT) { // STR_PAD_RIGHT == 1 + $str = $input.str_repeat($pad_string, $length); + } elseif ($pad_type == STR_PAD_BOTH) { // STR_PAD_BOTH == 2 + $str = str_repeat($pad_string, floor($length/2)); + $str .= $input; + $str .= str_repeat($pad_string, ceil($length/2)); + } else { // defaults to STR_PAD_LEFT == 0 + $str = str_repeat($pad_string, $length).$input; + } + + } else { // if $length is negative or zero we don't need to do anything + $str = $input; + } + return $str; +} + +/** + * Returns the cached array of LDAP resources. + * + * Note that internally, this function utilizes a two-layer cache, + * one in memory using a static variable for multiple calls within + * the same page load, and one in a session for multiple calls within + * the same user session (spanning multiple page loads). + * + * @return Returns the cached attributed requested, + * or null if there is nothing cached.. + */ +function get_cached_item($index,$item,$subitem='null') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Set default return + $return = null; + + # Check config to make sure session-based caching is enabled. + if ($_SESSION[APPCONFIG]->getValue('cache',$item) && isset($_SESSION['cache'][$index][$item][$subitem])) + $return = $_SESSION['cache'][$index][$item][$subitem]; + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; +} + +/** + * Caches the specified $item for the specified $index. + * + * Returns true on success of false on failure. + */ +function set_cached_item($index,$item,$subitem='null',$data) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Check config to make sure session-based caching is enabled. + if ($_SESSION[APPCONFIG]->getValue('cache',$item)) { + global $CACHE; + + $CACHE[$index][$item][$subitem] = $data; + $_SESSION['cache'][$index][$item][$subitem] = $data; + + return true; + + } else + return false; +} + +/** + * Deletes the cache for a specified $item for the specified $index + */ +function del_cached_item($index,$item,$subitem='null') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + global $CACHE; + + # Check config to make sure session-based caching is enabled. + if (isset($_SESSION['cache'][$index][$item][$subitem])) + unset($_SESSION['cache'][$index][$item][$subitem]); + + if (isset($CACHE[$index][$item][$subitem])) + unset($CACHE[$index][$item][$subitem]); +} + +/** + * Utility wrapper for setting cookies, which takes into consideration + * application configuration values. On success, true is returned. On + * failure, false is returned. + * + * @param string The name of the cookie to set. + * @param string The value of the cookie to set. + * @param int (optional) The duration in seconds of this cookie. If unspecified, $cookie_time is used from config.php + * @param string (optional) The directory value of this cookie (see php.net/setcookie) + * @return boolean + */ +function set_cookie($name,$val,$expire=null,$dir=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Set default return + $return = false; + + if ($expire == null) { + $cookie_time = $_SESSION[APPCONFIG]->getValue('session','cookie_time'); + $expire = $cookie_time == 0 ? null : time() + $cookie_time; + } + + if ($dir == null) + $dir = dirname($_SERVER['PHP_SELF']); + + if (@setcookie($name,$val,$expire,$dir)) { + $_COOKIE[$name] = $val; + $return = true; + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; +} + +/** + * Get a customized file for a server + * We don't need any caching, because it's done by PHP + * + * @param int The ID of the server + * @param string The requested filename + * + * @return string The customized filename, if exists, or the standard one + */ +function get_custom_file($index,$filename,$path) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Set default return + $return = $path.$filename; + $server = $_SESSION[APPCONFIG]->getServer($index); + + $custom = $server->getValue('custom','pages_prefix'); + if (! is_null($custom) && is_file(realpath($path.$custom.$filename))) + $return = $path.$custom.$filename; + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; +} + +/** + * Sort a multi dimensional array. + * + * @param array Multi demension array passed by reference + * @param string Comma delimited string of sort keys. + * @param boolean Whether to reverse sort. + * @return array Sorted multi demension array. + */ +function masort(&$data,$sortby,$rev=0) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # if the array to sort is null or empty + if (! $data) return; + + static $CACHE = array(); + + if (empty($CACHE[$sortby])) { + $code = "\$c=0;\n"; + + foreach (explode(',',$sortby) as $key) { + $code .= "if (is_object(\$a) || is_object(\$b)) {\n"; + + $code .= " if (is_array(\$a->$key)) {\n"; + $code .= " asort(\$a->$key);\n"; + $code .= " \$aa = array_shift(\$a->$key);\n"; + $code .= " } else\n"; + $code .= " \$aa = \$a->$key;\n"; + + $code .= " if (is_array(\$b->$key)) {\n"; + $code .= " asort(\$b->$key);\n"; + $code .= " \$bb = array_shift(\$b->$key);\n"; + $code .= " } else\n"; + $code .= " \$bb = \$b->$key;\n"; + + $code .= " if (\$aa != \$bb)"; + if ($rev) + $code .= " return (\$aa < \$bb ? 1 : -1);\n"; + else + $code .= " return (\$aa > \$bb ? 1 : -1);\n"; + + $code .= "} else {\n"; + + $code .= " \$a = array_change_key_case(\$a);\n"; + $code .= " \$b = array_change_key_case(\$b);\n"; + + $key = strtolower($key); + + $code .= " if ((! isset(\$a['$key'])) && isset(\$b['$key'])) return 1;\n"; + $code .= " if (isset(\$a['$key']) && (! isset(\$b['$key']))) return -1;\n"; + + $code .= " if ((isset(\$a['$key'])) && (isset(\$b['$key']))) {\n"; + $code .= " if (is_array(\$a['$key'])) {\n"; + $code .= " asort(\$a['$key']);\n"; + $code .= " \$aa = array_shift(\$a['$key']);\n"; + $code .= " } else\n"; + $code .= " \$aa = \$a['$key'];\n"; + + $code .= " if (is_array(\$b['$key'])) {\n"; + $code .= " asort(\$b['$key']);\n"; + $code .= " \$bb = array_shift(\$b['$key']);\n"; + $code .= " } else\n"; + $code .= " \$bb = \$b['$key'];\n"; + + $code .= " if (\$aa != \$bb)\n"; + $code .= " if (is_numeric(\$aa) && is_numeric(\$bb)) {\n"; + + if ($rev) + $code .= " return (\$aa < \$bb ? 1 : -1);\n"; + else + $code .= " return (\$aa > \$bb ? 1 : -1);\n"; + + $code .= " } else {\n"; + + if ($rev) + $code .= " if ( (\$c = strcasecmp(\$bb,\$aa)) != 0 ) return \$c;\n"; + else + $code .= " if ( (\$c = strcasecmp(\$aa,\$bb)) != 0 ) return \$c;\n"; + + $code .= " }\n"; + $code .= " }\n"; + $code .= "}\n"; + } + + $code .= 'return $c;'; + + $CACHE[$sortby] = create_function('$a, $b',$code); + } + + uasort($data,$CACHE[$sortby]); +} + +/** + * Is compression enabled for output + */ +function isCompress() { + return (isset($_SESSION[APPCONFIG]) && $_SESSION[APPCONFIG]->getValue('appearance','compress') + && ! ini_get('zlib.output_compression') + && preg_match('/gzip/',$_SERVER['HTTP_ACCEPT_ENCODING'])); +} + +/** + * PLA specific Functions + */ + +/** + * Fetches whether the user has configured phpLDAPadmin to obfuscate passwords + * with "*********" when displaying them. + * + * This is configured in config.php thus: + * + * $config->custom->appearance['obfuscate_password_display'] = true; + * + * + * Or if it is OK to show encrypted passwords but not clear text passwords + * + * $config->custom->appearance['show_clear_password'] = false; + * + * + * @param string Password encoding type + * @return boolean + */ +function obfuscate_password_display($enc=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($_SESSION[APPCONFIG]->getValue('appearance','obfuscate_password_display')) + $return = true; + + elseif (! $_SESSION[APPCONFIG]->getValue('appearance','show_clear_password') && (is_null($enc) || $enc == 'clear')) + $return = true; + + else + $return = false; + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; +} + +/** + * Returns an HTML-beautified version of a DN. + * Internally, this function makes use of pla_explode_dn() to break the + * the DN into its components. It then glues them back together with + * "pretty" HTML. The returned HTML is NOT to be used as a real DN, but + * simply displayed. + * + * @param string The DN to pretty-print. + * @return string + */ +function pretty_print_dn($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $dn_save = $dn; + $dn = pla_explode_dn($dn); + + if (! $dn) + return $dn_save; + + foreach ($dn as $i => $element) { + $element = htmlspecialchars($element); + $element = explode('=',$element,2); + $element = implode('=',$element); + $dn[$i] = $element; + } + + $dn = implode(',',$dn); + + return $dn; +} + +/** + * Given a string, this function returns true if the string has the format + * of a DN (ie, looks like "cn=Foo,dc=example,dc=com"). Returns false otherwise. + * The purpose of this function is so that developers can examine a string and + * know if it looks like a DN, and draw a hyperlink as needed. + * + * (See unit_test.php for test cases) + * + * @param string The attribute to examine for "DNness" + * @return boolean + */ +function is_dn_string($str) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + /* Try to break the string into its component parts if it can be done + ie, "uid=Manager" "dc=example" and "dc=com" */ + $parts = pla_explode_dn($str); + if (! is_array($parts) || ! count($parts)) + return false; + + /* Foreach of the "parts", look for an "=" character, + and make sure neither the left nor the right is empty */ + foreach ($parts as $part) { + if (! strpos($part,"=")) + return false; + + $sub_parts = explode('=',$part,2); + $left = $sub_parts[0]; + $right = $sub_parts[1]; + + if ( ! strlen(trim($left)) || ! strlen(trim($right))) + return false; + + if (strpos($left,'#') !== false) + return false; + } + + # We survived the above rigor. This is a bonified DN string. + return true; +} + +/** + * Get whether a string looks like an email address (user@example.com). + * + * @param string The string to analyze. + * @return boolean Returns true if the specified string looks like an email address or false otherwise. + */ +function is_mail_string($str) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $mail_regex = "/^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*$/"; + + if (preg_match($mail_regex,$str)) + return true; + else + return false; +} + +/** + * Get whether a string looks like a web URL (http://www.example.com/) + * + * @param string The string to analyze. + * @return boolean Returns true if the specified string looks like a web URL or false otherwise. + */ +function is_url_string($str) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $url_regex = '/^(ftp|https?):\/\/+[\w\.\-\/\?\=\&]*\w+/'; + + if (preg_match($url_regex,$str)) + return true; + else + return false; +} + +/** + * Compares 2 DNs. If they are equivelant, returns 0, otherwise, + * returns their sorting order (similar to strcmp()): + * Returns < 0 if dn1 is less than dn2. + * Returns > 0 if dn1 is greater than dn2. + * + * The comparison is performed starting with the top-most element + * of the DN. Thus, the following list: + * + * ou=people,dc=example,dc=com + * cn=Admin,ou=People,dc=example,dc=com + * cn=Joe,ou=people,dc=example,dc=com + * dc=example,dc=com + * cn=Fred,ou=people,dc=example,dc=org + * cn=Dave,ou=people,dc=example,dc=org + * + * Will be sorted thus using usort( $list, "pla_compare_dns" ): + * + * dc=com + * dc=example,dc=com + * ou=people,dc=example,dc=com + * cn=Admin,ou=People,dc=example,dc=com + * cn=Joe,ou=people,dc=example,dc=com + * cn=Dave,ou=people,dc=example,dc=org + * cn=Fred,ou=people,dc=example,dc=org + * + * + * @param string The first of two DNs to compare + * @param string The second of two DNs to compare + * @return int + */ +function pla_compare_dns($dn1,$dn2) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # If pla_compare_dns is passed via a tree, then we'll just get the DN part. + if (is_array($dn1)) + if (isset($dn1['dn'])) + $dn1 = $dn1['dn']; + else + $dn1 = implode('+',$dn1); + if (is_array($dn2)) + if (isset($dn2['dn'])) + $dn2 = $dn2['dn']; + else + $dn2 = implode('+',$dn2); + + # If they are obviously the same, return immediately + if (! strcasecmp($dn1,$dn2)) + return 0; + + $dn1_parts = pla_explode_dn(pla_reverse_dn($dn1)); + $dn2_parts = pla_explode_dn(pla_reverse_dn($dn2)); + assert(is_array($dn1_parts)); + assert(is_array($dn2_parts)); + + # Foreach of the "parts" of the smaller DN + for ($i=0; $i < count($dn1_parts) && $i < count($dn2_parts); $i++) { + /* dnX_part is of the form: "cn=joe" or "cn = joe" or "dc=example" + ie, one part of a multi-part DN. */ + $dn1_part = $dn1_parts[$i]; + $dn2_part = $dn2_parts[$i]; + + /* Each "part" consists of two sub-parts: + 1. the attribute (ie, "cn" or "o") + 2. the value (ie, "joe" or "example") */ + $dn1_sub_parts = explode('=',$dn1_part,2); + $dn2_sub_parts = explode('=',$dn2_part,2); + + $dn1_sub_part_attr = trim($dn1_sub_parts[0]); + $dn2_sub_part_attr = trim($dn2_sub_parts[0]); + + if (0 != ($cmp = strcasecmp($dn1_sub_part_attr,$dn2_sub_part_attr))) + return $cmp; + + $dn1_sub_part_val = trim($dn1_sub_parts[1]); + $dn2_sub_part_val = trim($dn2_sub_parts[1]); + if (0 != ($cmp = strcasecmp($dn1_sub_part_val,$dn2_sub_part_val))) + return $cmp; + } + + /* If we iterated through all entries in the smaller of the two DNs + (ie, the one with fewer parts), and the entries are different sized, + then, the smaller of the two must be "less than" than the larger. */ + if (count($dn1_parts) > count($dn2_parts)) { + return 1; + + } elseif (count($dn2_parts) > count($dn1_parts)) { + return -1; + + } else { + return 0; + } +} + +/** + * For LDAP servers with auto_number enabled, this function will get the next + * available number using the host's preferred mechanism (pool or search). + * + * This is configured in config.php by server: + * + * + * $servers->setValue('auto_number','enable',true|false); + * + * + * The available mechanisms are: + * pool: + * The pool mechanism uses a user-configured entry in the LDAP server to + * store the last used "number". This mechanism simply fetches and increments + * and returns that value. + * + * search: + * The search mechanism will search the LDAP server that has the attribute + * set. It will then find the smallest value and "fills in the gaps" by + * incrementing the smallest attribute until an unused value is found. + * + * NOTE: Both mechanisms do NOT prevent race conditions or toe-stomping, so + * care must be taken when actually creating the entry to check that the number + * returned here has not been used in the mean time. Note that the two different + * mechanisms may (will!) return different values as they use different algorithms + * to arrive at their result. Do not be alarmed if (when!) this is the case. + * + * See config.php.example for more notes on the two mechanisms. + * + * @param string Base to start the search from + * @param string Attribute to query + * @param boolean Increment the result (for pool searches) + * @param string LDAP filter to use (for pool searches) + * @return int + */ +function get_next_number($base,$attr,$increment=false,$filter=false,$startmin=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $_SESSION[APPCONFIG]->getServer(get_request('server_id','REQUEST')); + $attr = strtolower($attr); + $query = array(); + + if (! $server->getValue('auto_number','enable')) { + system_message(array( + 'title'=>_('AUTO_NUMBER is disabled for this server'), + 'body'=>sprintf('%s (%s)',_('A call was made to get_next_number(), however, it is disabled for this server'),$attr), + 'type'=>'warn')); + + return false; + } + + # Check see and use our alternate uid_dn and password if we have it. + if (! $server->login($server->getValue('auto_number','dn'),$server->getValue('auto_number','pass'),'auto_number')) { + system_message(array( + 'title'=>_('AUTO_NUMBER invalid login/password'), + 'body'=>sprintf('%s (%s)',_('Unable to connect to LDAP server with the auto_number login/password, please check your configuration.'), + $server->getName()), + 'type'=>'warn')); + + return false; + } + + # Some error checking + if (! $base) { + $query['base'] = $server->getValue('auto_number','search_base'); + + if (! trim($query['base'])) { + system_message(array( + 'title'=>_('No AUTO_NUMBER search_base configured for this server'), + 'body'=>_('A call was made to get_next_number(), however, the base to search is empty.'), + 'type'=>'warn')); + + return false; + } + + } else + $query['base'] = $base; + + if (! $server->dnExists($query['base'])) { + system_message(array( + 'title'=>_('No AUTO_NUMBER search_base exists for this server'), + 'body'=>sprintf('%s (%s)',_('A call was made to get_next_number(), however, the base to search does not exist for this server.'),$query['base']), + 'type'=>'warn')); + + return false; + } + + if (! is_string($attr) || ! $server->getSchemaAttribute($attr)) { + system_message(array( + 'title'=>_('AUTO_NUMBER search attribute invalid'), + 'body'=>sprintf('%s (%s)',_('The search attribute for AUTO_NUMBER is invalid, expecting a single valid attribute.'),$attr), + 'type'=>'warn')); + + return false; + } + + $query['attrs'] = array($attr); + + # Based on the configured mechanism, go get the next available uidNumber! + switch ($server->getValue('auto_number','mechanism')) { + case 'search': + $query['filter'] = sprintf('(%s=*)',$attr); + $search = $server->query($query,'auto_number'); + + # Construct a list of used numbers + $autonum = array(0); + + foreach ($search as $dn => $values) { + $values = array_change_key_case($values); + foreach ($values[$attr] as $value) + array_push($autonum,$value); + } + + $autonum = array_unique($autonum); + sort($autonum); + + # Start with the least existing autoNumber and add 1 + $minNumber = is_null($startmin) ? intval($autonum[0])+1 : $startmin; + + # Override our minNumber by the configuration if it exists. + if (count($server->getValue('auto_number','min'))) { + $min = array_change_key_case($server->getValue('auto_number','min')); + + if (isset($min[$attr])) + $minNumber = $min[$attr] > $minNumber ? $min[$attr] : $minNumber; + } + + for ($i=0;$i $num+1) + return $autonum[$i] >= $num ? $num+1 : $num; + } + + # If we didnt find a suitable gap and are all above the minNumber, we'll just return the $minNumber + return $minNumber; + + break; + + case 'pool': + switch ($attr) { + case 'gidnumber': + $query['filter'] = '(objectClass=gidPool)'; + + break; + + case 'uidnumber': + $query['filter'] = '(objectClass=uidPool)'; + + break; + } + + # If we are called with a filter, we'll use the one from the configuration. + if (! empty($filter)) + $query['filter'] = $filter; + + $search = $server->query($query,'auto_number'); + + switch (count($search)) { + case '1': + break; + + case '0': + system_message(array( + 'title'=>_('AUTO_NUMBER pool filter didnt return any DNs'), + 'body'=>sprintf('%s (%s)',_('Please change your filter parameter, or check your auto_number,search_base configuration'),$query['filter']), + 'type'=>'warn')); + + return false; + + default: + system_message(array( + 'title'=>_('AUTO_NUMBER pool filter returned too many DNs'), + 'body'=>sprintf('%s (%s)',_('Please change your filter parameter, or check your auto_number,search_base configuration'),$query['filter']), + 'type'=>'warn')); + + return false; + } + + # This should only iterate once. + foreach ($search as $dn => $values) { + $values = array_change_key_case($values); + + $autonum = $values[$attr][0]; + $poolDN = $values['dn']; + } + + if ($increment) { + $updatedattr = array($attr=>$autonum+1); + $server->modify($poolDN,$updatedattr); + } + + return $autonum; + + # No other cases allowed. The user has an error in the configuration + default: + system_message(array( + 'title'=>_('Invalid AUTO_NUMBER mechanism'), + 'body'=>sprintf('%s (%s)',_('Your config file specifies an unknown AUTO_NUMBER search mechanism.'),$server->getValue('auto_number','mechanism')), + 'type'=>'warn')); + + return false; + } +} + +/** + * Given a DN and server ID, this function reads the DN's objectClasses and + * determines which icon best represents the entry. The results of this query + * are cached in a session variable so it is not run every time the tree + * browser changes, just when exposing new DNs that were not displayed + * previously. That means we can afford a little bit of inefficiency here + * in favor of coolness. :) + * + * This function returns a string like "country.png". All icon files are assumed + * to be contained in the /images/ directory of phpLDAPadmin. + * + * Developers are encouraged to add new icons to the images directory and modify + * this function as needed to suit their types of LDAP entries. If the modifications + * are general to an LDAP audience, the phpLDAPadmin team will gladly accept them + * as a patch. + * + * @param string The DN of the entry whose icon you wish to fetch. + * @return string + */ +function get_icon($server_id,$dn,$object_classes=array()) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $_SESSION[APPCONFIG]->getServer($server_id); + + # Fetch and lowercase all the objectClasses in an array + if (! count($object_classes)) + $object_classes = $server->getDNAttrValue($dn,'objectClass'); + + foreach ($object_classes as $index => $value) + $object_classes[$index] = strtolower($value); + + $rdn = get_rdn($dn); + $rdn_parts = explode('=',$rdn,2); + $rdn_value = isset($rdn_parts[0]) ? $rdn_parts[0] : null; + $rdn_attr = isset($rdn_parts[1]) ? $rdn_parts[1] : null; + unset($rdn_parts); + + # Return icon filename based upon objectClass value + if (in_array('sambaaccount',$object_classes) && + '$' == $rdn{ strlen($rdn) - 1 }) + return 'nt_machine.png'; + + if (in_array('sambaaccount',$object_classes)) + return 'nt_user.png'; + + elseif (in_array('person',$object_classes) || + in_array('organizationalperson',$object_classes) || + in_array('inetorgperson',$object_classes) || + in_array('account',$object_classes) || + in_array('posixaccount',$object_classes)) + + return 'ldap-user.png'; + + elseif (in_array('organization',$object_classes)) + return 'ldap-o.png'; + + elseif (in_array('organizationalunit',$object_classes)) + return 'ldap-ou.png'; + + elseif (in_array('organizationalrole',$object_classes)) + return 'ldap-uid.png'; + + elseif (in_array('dcobject',$object_classes) || + in_array('domainrelatedobject',$object_classes) || + in_array('domain',$object_classes) || + in_array('builtindomain',$object_classes)) + + return 'ldap-dc.png'; + + elseif (in_array('alias',$object_classes)) + return 'ldap-alias.png'; + + elseif (in_array('room',$object_classes)) + return 'door.png'; + + elseif (in_array('device',$object_classes)) + return 'device.png'; + + elseif (in_array('document',$object_classes)) + return 'document.png'; + + elseif (in_array('country',$object_classes)) { + $tmp = pla_explode_dn($dn); + $cval = explode('=',$tmp[0],2); + $cval = isset($cval[1]) ? $cval[1] : false; + if ($cval && false === strpos($cval,'..') && + file_exists(realpath(sprintf('%s/../countries/%s.png',IMGDIR,strtolower($cval))))) + + return sprintf('../countries/%s.png',strtolower($cval)); + + else + return 'country.png'; + } + + elseif (in_array('jammvirtualdomain',$object_classes)) + return 'mail.png'; + + elseif (in_array('locality',$object_classes)) + return 'locality.png'; + + elseif (in_array('posixgroup',$object_classes) || + in_array('groupofnames',$object_classes) || + in_array('group',$object_classes)) + + return 'ldap-ou.png'; + + elseif (in_array('applicationprocess',$object_classes)) + return 'process.png'; + + elseif (in_array('groupofuniquenames',$object_classes)) + return 'ldap-uniquegroup.png'; + + elseif (in_array('iphost',$object_classes)) + return 'host.png'; + + elseif (in_array('nlsproductcontainer',$object_classes)) + return 'n.png'; + + elseif (in_array('ndspkikeymaterial',$object_classes)) + return 'lock.png'; + + elseif (in_array('server',$object_classes)) + return 'server-small.png'; + + elseif (in_array('volume',$object_classes)) + return 'hard-drive.png'; + + elseif (in_array('ndscatcatalog',$object_classes)) + return 'catalog.png'; + + elseif (in_array('resource',$object_classes)) + return 'n.png'; + + elseif (in_array('ldapgroup',$object_classes)) + return 'ldap-server.png'; + + elseif (in_array('ldapserver',$object_classes)) + return 'ldap-server.png'; + + elseif (in_array('nisserver',$object_classes)) + return 'ldap-server.png'; + + elseif (in_array('rbscollection',$object_classes)) + return 'ldap-ou.png'; + + elseif (in_array('dfsconfiguration',$object_classes)) + return 'nt_machine.png'; + + elseif (in_array('applicationsettings',$object_classes)) + return 'server-settings.png'; + + elseif (in_array('aspenalias',$object_classes)) + return 'mail.png'; + + elseif (in_array('container',$object_classes)) + return 'folder.png'; + + elseif (in_array('ipnetwork',$object_classes)) + return 'network.png'; + + elseif (in_array('samserver',$object_classes)) + return 'server-small.png'; + + elseif (in_array('lostandfound',$object_classes)) + return 'find.png'; + + elseif (in_array('infrastructureupdate',$object_classes)) + return 'server-small.png'; + + elseif (in_array('filelinktracking',$object_classes)) + return 'files.png'; + + elseif (in_array('automountmap',$object_classes) || + in_array('automount',$object_classes)) + + return 'hard-drive.png'; + + elseif (strpos($rdn_value,'ipsec') === 0 || + strcasecmp($rdn_value,'IP Security') == 0|| + strcasecmp($rdn_value,'MSRADIUSPRIVKEY Secret') == 0 || + strpos($rdn_value,'BCKUPKEY_') === 0) + + return 'lock.png'; + + elseif (strcasecmp($rdn_value,'MicrosoftDNS') == 0) + return 'ldap-dc.png'; + + # Oh well, I don't know what it is. Use a generic icon. + else + return 'ldap-default.png'; +} + +/** + * Appends a servers base to a "sub" dn or returns the base. + * + * @param string The baseDN to be added if the DN is relative + * @param string The DN to be made absolute + * @return string|null Returns null if both base is null and sub_dn is null or empty + */ +function expand_dn_with_base($base,$sub_dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $empty_str = (is_null($sub_dn) || (($len=strlen(trim($sub_dn))) == 0)); + + if ($empty_str) + return $base; + + # If we have a string which doesn't need a base + elseif ($sub_dn[$len-1] != ',') + return $sub_dn; + + else + return sprintf('%s%s',$sub_dn,$base); +} + +/** + * Used to generate a random salt for crypt-style passwords. Salt strings are used + * to make pre-built hash cracking dictionaries difficult to use as the hash algorithm uses + * not only the user's password but also a randomly generated string. The string is + * stored as the first N characters of the hash for reference of hashing algorithms later. + * + * @param int The length of the salt string to generate. + * @return string The generated salt string. + */ +function random_salt($length) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $possible = '0123456789'. + 'abcdefghijklmnopqrstuvwxyz'. + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + './'; + $str = ''; + mt_srand((double)microtime() * 1000000); + + while (strlen($str) < $length) + $str .= substr($possible,(rand()%strlen($possible)),1); + + return $str; +} + +/** + * Given a DN string, this returns the 'RDN' portion of the string. + * For example. given 'cn=Manager,dc=example,dc=com', this function returns + * 'cn=Manager' (it is really the exact opposite of ds_ldap::getContainer()). + * + * @param string The DN whose RDN to return. + * @param boolean If true, include attributes in the RDN string. See http://php.net/ldap_explode_dn for details + * @return string The RDN + */ +function get_rdn($dn,$include_attrs=0,$decode=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (is_null($dn)) + return null; + + $rdn = pla_explode_dn($dn,$include_attrs); + if (! count($rdn) || ! isset($rdn[0])) + return $dn; + + if ($decode) + $rdn = dn_unescape($rdn[0]); + else + $rdn = $rdn[0]; + + return $rdn; +} + +/** + * Split an RDN into its attributes + */ +function rdn_explode($rdn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Setup to work out our RDN. + $rdnarray = explode('\+',$rdn); + + # Capture items that have +, but are not an attribute + foreach ($rdnarray as $index => $val) { + if (preg_match('/=/',$val)) + $validindex = $index; + + if (! preg_match('/=/',$val)) { + $rdnarray[$validindex] .= '+'.$val; + unset($rdnarray[$index]); + } + } + + return $rdnarray; +} + +/** + * Given an LDAP error number, returns a verbose description of the error. + * This function parses ldap_error_codes.txt and looks up the specified + * ldap error number, and returns the verbose message defined in that file. + * + * + * Array ( + * [title] => "Invalid Credentials" + * [description] => "An invalid username and/or password was supplied to the LDAP server." + * ) + * + * + * @param string The hex error number (ie, "0x42") of the LDAP error of interest. + * @return array An associative array contianing the error title and description like so: + */ +function pla_verbose_error($key) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + static $CACHE = array(); + + if (! count($CACHE)) { + $source_file = LIBDIR.'ldap_error_codes.txt'; + + if (! file_exists($source_file) || ! is_readable($source_file) || ! ($f = fopen($source_file,'r'))) + return false; + + $contents = fread($f,filesize($source_file)); + fclose($f); + $entries = array(); + preg_match_all("/0x[A-Fa-f0-9][A-Za-z0-9]\s+[0-9A-Za-z_]+\s+\"[^\"]*\"\n/", + $contents,$entries); + + foreach ($entries[0] as $values) { + $entry = array(); + preg_match("/(0x[A-Za-z0-9][A-Za-z0-9])\s+([0-9A-Za-z_]+)\s+\"([^\"]*)\"/",$values,$entry); + + $hex_code = isset($entry[1]) ? $entry[1] : null; + $title = isset($entry[2]) ? $entry[2] : null; + $desc = isset($entry[3]) ? $entry[3] : null; + $desc = preg_replace('/\s+/',' ',$desc); + $CACHE[$hex_code] = array('title'=>$title,'desc'=>$desc); + } + } + + if (isset($CACHE[$key])) + return $CACHE[$key]; + else + return array('title' => null,'desc' => null); +} + +/** + * Given an LDAP OID number, returns a verbose description of the OID. + * This function parses ldap_supported_oids.txt and looks up the specified + * OID, and returns the verbose message defined in that file. + * + * + * Array ( + * [title] => All Operational Attribute + * [ref] => RFC 3673 + * [desc] => An LDAP extension which clients may use to request the return of all operational attributes. + * ) + * + * + * @param string The OID number (ie, "1.3.6.1.4.1.4203.1.5.1") of the OID of interest. + * @return array An associative array contianing the OID title and description like so: + */ +function support_oid_to_text($key) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + static $CACHE = array(); + + $unknown = array(); + $unknown['desc'] = 'We have no description for this OID, if you know what this OID provides, please let us know. Please also include an RFC reference if it is available.'; + $unknown['title'] = 'Can you help with this OID info?'; + + if (! count($CACHE)) { + $source_file = LIBDIR.'ldap_supported_oids.txt'; + + if (! file_exists($source_file) || ! is_readable($source_file) || ! ($f = fopen($source_file,'r'))) + return false; + + $contents = fread($f,filesize($source_file)); + fclose($f); + $entries = array(); + preg_match_all("/[0-9]\..+\s+\"[^\"]*\"\n/",$contents,$entries); + + foreach ($entries[0] as $values) { + $entry = array(); + preg_match("/([0-9]\.([0-9]+\.)*[0-9]+)(\s+\"([^\"]*)\")?(\s+\"([^\"]*)\")?(\s+\"([^\"]*)\")?/",$values,$entry); + $oid_id = isset($entry[1]) ? $entry[1] : null; + + if ($oid_id) { + $CACHE[$oid_id]['title'] = isset($entry[4]) ? $entry[4] : null; + $CACHE[$oid_id]['ref'] = isset($entry[6]) ? $entry[6] : null; + $desc = isset($entry[8]) ? $entry[8] : sprintf('%s',$unknown['desc'],$unknown['title']); + $CACHE[$oid_id]['desc'] = preg_replace('/\s+/',' ',$desc); + } + } + } + + if (isset($CACHE[$key])) + return $CACHE[$key]; + else + return array( + 'title'=>$key, + 'ref'=>null, + 'desc'=>sprintf('%s',$unknown['desc'],$unknown['title'])); +} + +/** + * Print an LDAP error message + */ +function ldap_error_msg($msg,$errnum) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $body = ''; + + $errnum = ('0x'.str_pad(dechex($errnum),2,0,STR_PAD_LEFT)); + $verbose_error = pla_verbose_error($errnum); + + $body .= sprintf('',_('LDAP said'),$msg); + + if ($verbose_error) { + $body .= sprintf('',_('Error number'),$errnum,$verbose_error['title']); + $body .= sprintf('',_('Description'),$verbose_error['desc']); + + } else { + $body .= sprintf('',_('Error number'),$errnum); + $body .= sprintf('',_('Description'),_('no description available')); + } + + $body .= '
%s:%s
%s:%s (%s)
%s:%s
%s:%s
%s:(%s)
'; + + return $body; +} + +/** + * Draw the jpegPhoto image(s) for an entry wrapped in HTML. Many options are available to + * specify how the images are to be displayed. + * + * Usage Examples: + * + * draw_jpeg_photo(0,'cn=Bob,ou=People,dc=example,dc=com',"jpegPhoto",0,true,array('img_opts'=>"border: 1px; width: 150px")); + * draw_jpeg_photo(1,'cn=Fred,ou=People,dc=example,dc=com',null,1); + * + * + * @param object The Server to get the image from. + * @param string The DN of the entry that contains the jpeg attribute you want to draw. + * @param string The name of the attribute containing the jpeg data (usually 'jpegPhoto'). + * @param int Index of the attribute to draw + * @param boolean If true, draws a button beneath the image titled 'Delete' allowing the user + * to delete the jpeg attribute by calling JavaScript function deleteAttribute() provided + * in the default modification template. + * @param array Specifies optional image and CSS style attributes for the table tag. Supported keys are + * fixed_width, fixed_height, img_opts. + */ +function draw_jpeg_photo($server,$dn,$attr_name='jpegphoto',$index,$draw_delete_buttons=false,$options=array()) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $fixed = array(); + $fixed['width'] = isset($options['fixed_width']) ? $options['fixed_width'] : false; + $fixed['height'] = isset($options['fixed_height']) ? $options['fixed_height'] : false; + + if (is_null($server)) + $jpeg_data = $_SESSION['tmp']; + else + $jpeg_data = $server->getDNAttrValues($dn,null,LDAP_DEREF_NEVER,array($attr_name)); + + if (! isset($jpeg_data[$attr_name][$index]) || ! $jpeg_data[$attr_name][$index]) { + system_message(array( + 'title'=>_('Unable to retrieve image'), + 'body'=>sprintf('%s %s', + _('Could not fetch jpeg data for attribute'),$attr_name), + 'type'=>'warn')); + + # This should atleast generate some text that says "Image not available" + printf('Photo',$attr_name); + + return; + } + + $width = 0; + $height = 0; + + if (function_exists('getimagesize')) { + $jpeg_temp_dir = realpath($_SESSION[APPCONFIG]->getValue('jpeg','tmpdir').'/'); + if (! is_writable($jpeg_temp_dir)) + system_message(array( + 'title'=>_('Unable to write to jpeg tmp directory'), + 'body'=>_('Please set jpeg,tmpdir to a writable directory in the phpLDAPadmin config.php'), + 'type'=>'warn')); + + else { + # We have an image to display + $jpeg_filename = tempnam($jpeg_temp_dir.'/','pla'); + $outjpeg = @file_put_contents($jpeg_filename,$jpeg_data[$attr_name][$index]); + + if (! $outjpeg) { + system_message(array( + 'title'=>_('Error writing to jpeg tmp directory'), + 'body'=>sprintf(_('Please check jpeg,tmpdir is a writable directory in the phpLDAPadmin config.php'),$jpeg_temp_dir), + 'type'=>'warn')); + + } elseif ($outjpeg < 6) { + system_message(array( + 'title'=>sprintf('%s %s',$attr_name,_('contains errors')), + 'body'=>_('It appears that the jpeg image may not be a jpeg image'), + 'type'=>'warn')); + + } else { + $jpeg_dimensions = getimagesize($jpeg_filename); + $width = $jpeg_dimensions[0]; + $height = $jpeg_dimensions[1]; + } + + unlink($jpeg_filename); + } + } + + if ($width > 300) { + $scale_factor = 300 / $width; + $img_width = 300; + $img_height = intval($height * $scale_factor); + + } else { + $img_width = $width; + $img_height = $height; + } + + $href = sprintf('view_jpeg_photo.php?dn=%s&index=%s&attr=%s',rawurlencode($dn),$index,$attr_name); + + printf('',number_format($outjpeg),_('bytes'),$width,$height,_('pixels')); + + printf('Photo', + htmlspecialchars($href), + is_null($server) ? 'location=session' : sprintf('server_id=%s',$server->getIndex()), + (! $img_width || $fixed['width'] ? '' : sprintf('width="%s"',$img_width)), + (! $img_height || $fixed['height'] ? '' : sprintf('height="%s"',$img_height)), + (isset($options['img_opts']) ? $options['img_opts'] : '')); + + echo ''; + + if ($draw_delete_buttons) + # + printf('
%s', + $attr_name,_('Delete photo')); +} + +/** + * Return the list of available password types + * + * @todo Dynamically work this list out so we only present hashes that we can encrypt + */ +function password_types() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return array( + ''=>'clear', + 'blowfish'=>'blowfish', + 'crypt'=>'crypt', + 'ext_des'=>'ext_des', + 'md5'=>'md5', + 'k5key'=>'k5key', + 'md5crypt'=>'md5crypt', + 'sha'=>'sha', + 'smd5'=>'smd5', + 'ssha'=>'ssha' + ); +} + +/** + * Hashes a password and returns the hash based on the specified enc_type. + * + * @param string The password to hash in clear text. + * @param string Standard LDAP encryption type which must be one of + * crypt, ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, or clear. + * @return string The hashed password. + */ +function password_hash($password_clear,$enc_type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $enc_type = strtolower($enc_type); + + switch($enc_type) { + case 'blowfish': + if (! defined('CRYPT_BLOWFISH') || CRYPT_BLOWFISH == 0) + error(_('Your system crypt library does not support blowfish encryption.'),'error','index.php'); + + # Hardcoded to second blowfish version and set number of rounds + $new_value = sprintf('{CRYPT}%s',crypt($password_clear,'$2a$12$'.random_salt(13))); + + break; + + case 'crypt': + if ($_SESSION[APPCONFIG]->getValue('password', 'no_random_crypt_salt')) + $new_value = sprintf('{CRYPT}%s',crypt($password_clear,substr($password_clear,0,2))); + else + $new_value = sprintf('{CRYPT}%s',crypt($password_clear,random_salt(2))); + + break; + + case 'ext_des': + # Extended des crypt. see OpenBSD crypt man page. + if (! defined('CRYPT_EXT_DES') || CRYPT_EXT_DES == 0) + error(_('Your system crypt library does not support extended DES encryption.'),'error','index.php'); + + $new_value = sprintf('{CRYPT}%s',crypt($password_clear,'_'.random_salt(8))); + + break; + + case 'k5key': + $new_value = sprintf('{K5KEY}%s',$password_clear); + + system_message(array( + 'title'=>_('Unable to Encrypt Password'), + 'body'=>'phpLDAPadmin cannot encrypt K5KEY passwords', + 'type'=>'warn')); + + break; + + case 'md5': + $new_value = sprintf('{MD5}%s',base64_encode(pack('H*',md5($password_clear)))); + break; + + case 'md5crypt': + if (! defined('CRYPT_MD5') || CRYPT_MD5 == 0) + error(_('Your system crypt library does not support md5crypt encryption.'),'error','index.php'); + + $new_value = sprintf('{CRYPT}%s',crypt($password_clear,'$1$'.random_salt(9))); + + break; + + case 'sha': + # Use php 4.3.0+ sha1 function, if it is available. + if (function_exists('sha1')) + $new_value = sprintf('{SHA}%s',base64_encode(pack('H*',sha1($password_clear)))); + elseif (function_exists('mhash')) + $new_value = sprintf('{SHA}%s',base64_encode(mhash(MHASH_SHA1,$password_clear))); + else + error(_('Your PHP install does not have the mhash() function. Cannot do SHA hashes.'),'error','index.php'); + + break; + + case 'ssha': + if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { + mt_srand((double)microtime()*1000000); + $salt = mhash_keygen_s2k(MHASH_SHA1,$password_clear,substr(pack('h*',md5(mt_rand())),0,8),4); + $new_value = sprintf('{SSHA}%s',base64_encode(mhash(MHASH_SHA1,$password_clear.$salt).$salt)); + + } else { + error(_('Your PHP install does not have the mhash() or mhash_keygen_s2k() function. Cannot do S2K hashes.'),'error','index.php'); + } + + break; + + case 'smd5': + if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { + mt_srand((double)microtime()*1000000); + $salt = mhash_keygen_s2k(MHASH_MD5,$password_clear,substr(pack('h*',md5(mt_rand())),0,8),4); + $new_value = sprintf('{SMD5}%s',base64_encode(mhash(MHASH_MD5,$password_clear.$salt).$salt)); + + } else { + error(_('Your PHP install does not have the mhash() or mhash_keygen_s2k() function. Cannot do S2K hashes.'),'error','index.php'); + } + + break; + + case 'clear': + default: + $new_value = $password_clear; + } + + return $new_value; +} + +/** + * Given a clear-text password and a hash, this function determines if the clear-text password + * is the password that was used to generate the hash. This is handy to verify a user's password + * when all that is given is the hash and a "guess". + * @param String The hash. + * @param String The password in clear text to test. + * @return Boolean True if the clear password matches the hash, and false otherwise. + */ +function password_check($cryptedpassword,$plainpassword,$attribute='userpassword') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (in_array($attribute,array('sambalmpassword','sambantpassword'))) { + $smb = new smbHash; + + switch($attribute) { + case 'sambalmpassword': + if (strcmp($smb->lmhash($plainpassword),$cryptedpassword) == 0) + return true; + else + return false; + + case 'sambantpassword': + if (strcmp($smb->nthash($plainpassword),$cryptedpassword) == 0) + return true; + else + return false; + } + + return false; + } + + if (preg_match('/{([^}]+)}(.*)/',$cryptedpassword,$matches)) { + $cryptedpassword = $matches[2]; + $cypher = strtolower($matches[1]); + + } else { + $cypher = null; + } + + switch($cypher) { + # SSHA crypted passwords + case 'ssha': + # Check php mhash support before using it + if (function_exists('mhash')) { + $hash = base64_decode($cryptedpassword); + + # OpenLDAP uses a 4 byte salt, SunDS uses an 8 byte salt - both from char 20. + $salt = substr($hash,20); + $new_hash = base64_encode(mhash(MHASH_SHA1,$plainpassword.$salt).$salt); + + if (strcmp($cryptedpassword,$new_hash) == 0) + return true; + else + return false; + + } else { + error(_('Your PHP install does not have the mhash() function. Cannot do SHA hashes.'),'error','index.php'); + } + + break; + + # Salted MD5 + case 'smd5': + # Check php mhash support before using it + if (function_exists('mhash')) { + $hash = base64_decode($cryptedpassword); + $salt = substr($hash,16); + $new_hash = base64_encode(mhash(MHASH_MD5,$plainpassword.$salt).$salt); + + if (strcmp($cryptedpassword,$new_hash) == 0) + return true; + else + return false; + + } else { + error(_('Your PHP install does not have the mhash() function. Cannot do SHA hashes.'),'error','index.php'); + } + + break; + + # SHA crypted passwords + case 'sha': + if (strcasecmp(password_hash($plainpassword,'sha'),'{SHA}'.$cryptedpassword) == 0) + return true; + else + return false; + + break; + + # MD5 crypted passwords + case 'md5': + if( strcasecmp(password_hash($plainpassword,'md5'),'{MD5}'.$cryptedpassword) == 0) + return true; + else + return false; + + break; + + # Crypt passwords + case 'crypt': + # Check if it's blowfish crypt + if (preg_match('/^\\$2+/',$cryptedpassword)) { + + # Make sure that web server supports blowfish crypt + if (! defined('CRYPT_BLOWFISH') || CRYPT_BLOWFISH == 0) + error(_('Your system crypt library does not support blowfish encryption.'),'error','index.php'); + + list($version,$rounds,$salt_hash) = explode('$',$cryptedpassword); + + if (crypt($plainpassword,'$'.$version.'$'.$rounds.'$'.$salt_hash) == $cryptedpassword) + return true; + else + return false; + } + + # Check if it's an crypted md5 + elseif (strstr($cryptedpassword,'$1$')) { + + # Make sure that web server supports md5 crypt + if (! defined('CRYPT_MD5') || CRYPT_MD5 == 0) + error(_('Your system crypt library does not support md5crypt encryption.'),'error','index.php'); + + list($dummy,$type,$salt,$hash) = explode('$',$cryptedpassword); + + if (crypt($plainpassword,'$1$'.$salt) == $cryptedpassword) + return true; + else + return false; + } + + # Check if it's extended des crypt + elseif (strstr($cryptedpassword,'_')) { + + # Make sure that web server supports ext_des + if (! defined('CRYPT_EXT_DES') || CRYPT_EXT_DES == 0) + error(_('Your system crypt library does not support extended DES encryption.'),'error','index.php'); + + if (crypt($plainpassword,$cryptedpassword) == $cryptedpassword) + return true; + else + return false; + } + + # Password is plain crypt + else { + + if (crypt($plainpassword,$cryptedpassword) == $cryptedpassword) + return true; + else + return false; + } + + break; + + # No crypt is given assume plaintext passwords are used + default: + if ($plainpassword == $cryptedpassword) + return true; + else + return false; + } +} + +/** + * Detects password encryption type + * + * Returns crypto string listed in braces. If it is 'crypt' password, + * returns crypto detected in password hash. Function should detect + * md5crypt, blowfish and extended DES crypt. If function fails to detect + * encryption type, it returns NULL. + * @param string Hashed password + * @return string + */ +function get_enc_type($user_password) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Capture the stuff in the { } to determine if this is crypt, md5, etc. + $enc_type = null; + + if (preg_match('/{([^}]+)}/',$user_password,$enc_type)) + $enc_type = strtolower($enc_type[1]); + else + return null; + + # Handle crypt types + if (strcasecmp($enc_type,'crypt') == 0) { + + # No need to check for standard crypt, because enc_type is already equal to 'crypt'. + if (preg_match('/{[^}]+}\\$1\\$+/',$user_password)) + $enc_type = 'md5crypt'; + + elseif (preg_match('/{[^}]+}\\$2+/',$user_password)) + $enc_type = 'blowfish'; + + elseif (preg_match('/{[^}]+}_+/',$user_password)) + $enc_type = 'ext_des'; + } + + return $enc_type; +} + +/** + * Draws an HTML browse button which, when clicked, pops up a DN chooser dialog. + * @param string The name of the form element to which this chooser + * dialog will publish the user's choice. The form element must be a member + * of a form with the "name" or "id" attribute set in the form tag, and the element + * must also define "name" or "id" for JavaScript to uniquely identify it. + * Example $form_element values may include "creation_form.container" or + * "edit_form.member_uid". See /templates/modification/default.php for example usage. + * @param boolean (optional) If true, the function draws the localized text "choose" to the right of the button. + */ +function draw_chooser_link($form,$element,$include_choose_text=true,$rdn='none') { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $href = sprintf("javascript:dnChooserPopup('%s','%s','%s');",$form,$element,$rdn == 'none' ? '' : rawurlencode($rdn)); + $title = _('Click to popup a dialog to select an entry (DN) graphically'); + + printf('Find',$href,$title,IMGDIR); + + if ($include_choose_text) + printf('%s',$href,$title,_('browse')); +} + +/** + * Explode a DN into an array of its RDN parts. + * + * NOTE: When a multivalue RDN is passed to ldap_explode_dn, the results returns with 'value + value'; + * + * + * Array ( + * [0] => uid=ppratt + * [1] => ou=People + * [2] => dc=example + * [3] => dc=com + * ) + * + * + * @param string The DN to explode. + * @param int (optional) Whether to include attribute names (see http://php.net/ldap_explode_dn for details) + * @return array An array of RDN parts of this format: + */ +function pla_explode_dn($dn,$with_attributes=0) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + global $CACHE; + + if (isset($CACHE['explode'][$dn][$with_attributes])) { + if (DEBUG_ENABLED) + debug_log('Return CACHED result (%s) for (%s)',1,0,__FILE__,__LINE__,__METHOD__, + $CACHE['explode'][$dn][$with_attributes],$dn); + + return $CACHE['explode'][$dn][$with_attributes]; + } + + $dn = addcslashes($dn,'<>+";'); + + # split the dn + $result[0] = ldap_explode_dn(dn_escape($dn),0); + $result[1] = ldap_explode_dn(dn_escape($dn),1); + if (! $result[$with_attributes]) { + if (DEBUG_ENABLED) + debug_log('Returning NULL - NO result.',1,0,__FILE__,__LINE__,__METHOD__); + + return array(); + } + + # Remove our count value that ldap_explode_dn returns us. + unset($result[0]['count']); + unset($result[1]['count']); + + # Record the forward and reverse entries in the cache. + foreach ($result as $key => $value) { + # translate hex code into ascii for display + $result[$key] = dn_unescape($value); + + $CACHE['explode'][implode(',',$result[0])][$key] = $result[$key]; + $CACHE['explode'][implode(',',array_reverse($result[0]))][$key] = array_reverse($result[$key]); + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$result[$with_attributes]); + + return $result[$with_attributes]; +} + +/** + * Parse a DN and escape any special characters + */ +function dn_escape($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $olddn = $dn; + + # Check if the RDN has a comma and escape it. + while (preg_match('/([^\\\\]),(\s*[^=]*\s*),/',$dn)) + $dn = preg_replace('/([^\\\\]),(\s*[^=]*\s*),/','$1\\\\2C$2,',$dn); + + $dn = preg_replace('/([^\\\\]),(\s*[^=]*\s*)([^,])$/','$1\\\\2C$2$3',$dn); + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$dn); + + return $dn; +} + +/** + * Parse a DN and unescape any special characters + */ +function dn_unescape($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (is_array($dn)) { + $a = array(); + + foreach ($dn as $key => $rdn) + $a[$key] = preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$rdn); + + return $a; + + } else { + return preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$dn); + } +} + +/** + * Fetches the URL for the specified item. This is a convenience function for + * fetching project HREFs (like bugs) + * + * @param string One of "open_bugs", "add_bug", "donate", or "add_rfe" + * (rfe = request for enhancement) + * @return string The URL to the requested item. + */ +function get_href($type,$extra_info='') { + $sf = 'https://sourceforge.net'; + $pla = 'http://phpldapadmin.sourceforge.net'; + $group_id = '61828'; + $bug_atid = '498546'; + $rfe_atid = '498549'; + $forum_id = 'phpldapadmin-users'; + + switch($type) { + case 'add_bug': + return sprintf('%s/tracker/?func=add&group_id=%s&atid=%s',$sf,$group_id,$bug_atid); + case 'add_rfe': + return sprintf('%s/tracker/?func=add&group_id=%s&atid=%s',$sf,$group_id,$rfe_atid); + case 'credits': + return sprintf('%s/Credits',$pla); + case 'documentation': + return sprintf('%s/Documentation',$pla); + case 'donate': + return sprintf('%s/donate/index.php?group_id=%s',$sf,$group_id); + case 'forum': + return sprintf('%s/mailarchive/forum.php?forum_name=%s',$sf,$forum_id); + case 'logo': + if (! isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on') + $proto = 'http'; + else + $proto = 'https'; + + return isset($_SESSION) && ! $_SESSION[APPCONFIG]->getValue('appearance','remoteurls') ? '' : sprintf('%s://sflogo.sourceforge.net/sflogo.php?group_id=%s&type=10',$proto,$group_id); + case 'sf': + return sprintf('%s/projects/phpldapadmin',$sf); + case 'web': + return sprintf('%s',$pla); + default: + return null; + } +} + +/** + * Returns the current time as a double (including micro-seconds). + * + * @return double The current time in seconds since the beginning of the UNIX epoch (Midnight Jan. 1, 1970) + */ +function utime() { + $time = explode(' ',microtime()); + $usec = (double)$time[0]; + $sec = (double)$time[1]; + return $sec + $usec; +} + +/** + * Converts an array to a query-string with the option to exclude certain variables + * from the returned query string. This is convenient if callers want to convert the + * current GET query string or POST array into a string and replace certain + * variables with their own. + * + * @param array The associate array to convert whose form is such that the keys are the + * names of the variables and the values are said variables' values like this: + * + * Array ( + * [server_id] = 0, + * [dn] = "dc=example,dc=com", + * [attr] = "sn" + * ) + * + * This will produce a string like this: "server_id=0&dn=dc=example,dc=com&attr=sn" + * @param array (optional) An array of variables to exclude in the resulting string + * @return string The string created from the array. + */ +function array_to_query_string($array,$exclude_vars=array()) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_array($array) || ! count($array)) + return ''; + + $str = ''; + $i = 0; + foreach ($array as $name => $val) + if (! in_array($name,$exclude_vars)) + if (is_array($val)) + foreach ($val as $v) { + if ($i++ > 0) + $str .= '&'; + + $str .= sprintf('%s[]=%s',rawurlencode($name),rawurlencode($v)); + } + + else { + if ($i++ > 0) + $str .= '&'; + + $str .= sprintf('%s=%s',rawurlencode($name),rawurlencode($val)); + } + + return $str; +} + +/** + * Reverses a DN such that the top-level RDN is first and the bottom-level RDN is last + * For example: + * + * cn=Brigham,ou=People,dc=example,dc=com + * + * Becomes: + * + * dc=com,dc=example,ou=People,cn=Brigham + * + * This makes it possible to sort lists of DNs such that they are grouped by container. + * + * @param string The DN to reverse + * @return string The reversed DN + * + * @see pla_compare_dns + * @see pla_explode_dns + */ +function pla_reverse_dn($dn) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return (implode(',',array_reverse(pla_explode_dn($dn)))); +} + +/** + * Attribute sorting + */ +function sortAttrs($a,$b) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($a == $b) + return 0; + + $server = $_SESSION[APPCONFIG]->getServer(get_request('server_id','REQUEST')); + $attrs_display_order = arrayLower($_SESSION[APPCONFIG]->getValue('appearance','attr_display_order')); + + # Check if $a is in $attrs_display_order, get its key + $a_key = array_search($a->getName(),$attrs_display_order); + $b_key = array_search($b->getName(),$attrs_display_order); + + if ((! $a_key) && ($a_key !== 0)) + if ((! $a_key = array_search(strtolower($a->getFriendlyName()),$attrs_display_order)) && ($a_key !== 0)) + $a_key = count($attrs_display_order)+1; + + if ((! $b_key) && ($b_key !== 0)) + if ((! $b_key = array_search(strtolower($b->getFriendlyName()),$attrs_display_order)) && ($b_key !== 0)) + $b_key = count($attrs_display_order)+1; + + # Case where neither $a, nor $b are in $attrs_display_order, $a_key = $b_key = one greater than num elements. + # So we sort them alphabetically + if ($a_key === $b_key) + return strcasecmp($a->getFriendlyName(),$b->getFriendlyName()); + + # Case where at least one attribute or its friendly name is in $attrs_display_order + # return -1 if $a before $b in $attrs_display_order + return ($a_key < $b_key) ? -1 : 1; +} + +/** + * Reads an array and returns the array values back in lower case + * + * @param array $array The array to convert the values to lowercase. + * @returns array Array with values converted to lowercase. + */ +function arrayLower($array) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_array($array)) + return $array; + + $newarray = array(); + foreach ($array as $key => $value) + $newarray[$key] = strtolower($value); + + return $newarray; +} + +/** + * Gets a DN string using the user-configured tree_display_format string to format it. + */ +function draw_formatted_dn($server,$entry) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $dn = $entry->getDn(); + + $formats = $_SESSION[APPCONFIG]->getValue('appearance','tree_display_format'); + + foreach ($formats as $format) { + $has_none = false; + preg_match_all('/%[a-zA-Z_0-9]+/',$format,$tokens); + $tokens = $tokens[0]; + + if (DEBUG_ENABLED) + debug_log('The tokens are (%s)',1,0,__FILE__,__LINE__,__METHOD__,$tokens); + + foreach ($tokens as $token) { + if (strcasecmp($token,'%dn') == 0) + $format = str_replace($token,pretty_print_dn($dn),$format); + + elseif (strcasecmp($token,'%rdn') == 0) + $format = str_replace($token,pretty_print_dn($entry->getRDN()),$format); + + elseif (strcasecmp($token,'%rdnvalue') == 0) { + $rdn = get_rdn($dn,0,true); + $rdn_value = explode('=',$rdn,2); + $rdn_value = $rdn_value[1]; + $format = str_replace($token,$rdn_value,$format); + + } else { + $attr_name = str_replace('%','',$token); + $attr_values = $server->getDNAttrValue($dn,$attr_name); + + if (is_null($attr_values) || (count($attr_values) <= 0)) { + $display = '<'._('none').'>'; + $has_none = true; + + } elseif (is_array($attr_values)) + $display = implode(', ',$attr_values); + + else + $display = $attr_values; + + $format = str_replace($token,$display,$format); + } + } + + # If this format has all values available, use it. Otherwise, try the next one + if (!$has_none) + return $format; + } + + return $format; +} + +/** + * Server html select list + */ +function server_select_list($selected=null,$logged_on=false,$name='index',$isVisible=true,$js=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $count = 0; + $server_menu_html = sprintf(''; + + if ($count > 1) + return $server_menu_html; + + elseif ($count) + return sprintf('%s ', + $selected_server->getName(),$name,$selected_server->getIndex()); + + else + return ''; +} + +/** + * Converts a little-endian hex-number to one, that 'hexdec' can convert + */ +function littleEndian($hex) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = ''; + + for ($x=strlen($hex)-2;$x>= 0;$x=$x-2) + $result .= substr($hex,$x,2); + + return $result; +} + +function binSIDtoText($binsid) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $hex_sid = bin2hex($binsid); + $rev = hexdec(substr($hex_sid,0,2)); // Get revision-part of SID + $subcount = hexdec(substr($hex_sid,2,2)); // Get count of sub-auth entries + $auth = hexdec(substr($hex_sid,4,12)); // SECURITY_NT_AUTHORITY + + $result = "$rev-$auth"; + + for ($x=0;$x<$subcount;$x++) { + $subauth[$x] = hexdec(littleEndian(substr($hex_sid,16+($x*8),8))); // get all SECURITY_NT_AUTHORITY + $result .= sprintf('-%s',$subauth[$x]); + } + + return $result; +} + +/** + * Query LDAP and return a hash. + * + * @param string The base DN to use. + * @param string LDAP Query filter. + * @param string LDAP attribute to use as key. + * @param array Attributes to use as values. + * @param boolean Specify false to not sort results by DN + * or true to have the returned array sorted by DN (uses ksort) + * or an array of attribute names to sort by attribute values + * @return array Array of values keyed by $key. + */ +function return_ldap_hash($base,$filter,$key,$attrs,$sort=true) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $server = $_SESSION[APPCONFIG]->getServer(get_request('server_id','REQUEST')); + $key = strtolower($key); + + $query = array(); + $query['base'] = $base; + $query['filter'] = $filter; + $query['attrs'] = $attrs; + $search = $server->query($query,null); + + $results = array(); + + foreach ($search as $dn => $values) + if (isset($values[$key])) + if (is_array($values[$key])) + foreach ($values[$key] as $i => $k) + foreach ($attrs as $attr) { + $lattr = strtolower($attr); + if (isset($values[$lattr])) { + $v = ''; + + if (is_array($values[$lattr]) && isset($values[$lattr][$i])) + $v = $values[$lattr][$i]; + + if (is_string($v) && (strlen($v) > 0)) + $results[$k][$attr] = $v; + } + } + + else + foreach ($attrs as $attr) { + $lattr = strtolower($attr); + if (isset($values[$lattr])) + $results[$values[$key]][$attr] = $values[$lattr]; + } + + if ($sort) + masort($results,is_array($sort) ? implode(',',$sort) : 'dn'); + + return $results; +} + +/** + * This function returns a string automatically generated + * based on the criteria defined in the array $criteria in config.php + */ +function password_generate() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $no_use_similiar = ! $_SESSION[APPCONFIG]->getValue('password','use_similar'); + $lowercase = $_SESSION[APPCONFIG]->getValue('password','lowercase'); + $uppercase = $_SESSION[APPCONFIG]->getValue('password','uppercase'); + $digits = $_SESSION[APPCONFIG]->getValue('password','numbers'); + $punctuation = $_SESSION[APPCONFIG]->getValue('password','punctuation'); + $length = $_SESSION[APPCONFIG]->getValue('password','length'); + + $outarray = array(); + + if ($no_use_similiar) { + $raw_lower = 'a b c d e f g h k m n p q r s t u v w x y z'; + $raw_numbers = '2 3 4 5 6 7 8 9'; + $raw_punc = '# $ % ^ & * ( ) _ - + = . , [ ] { } :'; + + } else { + $raw_lower = 'a b c d e f g h i j k l m n o p q r s t u v w x y z'; + $raw_numbers = '1 2 3 4 5 6 7 8 9 0'; + $raw_punc = '# $ % ^ & * ( ) _ - + = . , [ ] { } : |'; + } + + $llower = explode(' ',$raw_lower); + shuffle($llower); + $lupper = explode(' ',strtoupper($raw_lower)); + shuffle($lupper); + $numbers = explode(' ',$raw_numbers); + shuffle($numbers); + $punc = explode(' ',$raw_punc); + shuffle($punc); + + if ($lowercase > 0) + $outarray = array_merge($outarray,a_array_rand($llower,$lowercase)); + + if ($uppercase > 0) + $outarray = array_merge($outarray,a_array_rand($lupper,$uppercase)); + + if ($digits > 0) + $outarray = array_merge($outarray,a_array_rand($numbers,$digits)); + + if ($punctuation > 0) + $outarray = array_merge($outarray,a_array_rand($punc,$punctuation)); + + $num_spec = $lowercase + $uppercase + $digits + $punctuation; + + if ($num_spec < $length) { + $leftover = array(); + if ($lowercase > 0) + $leftover = array_merge($leftover,$llower); + if ($uppercase > 0) + $leftover = array_merge($leftover,$lupper); + if ($digits > 0) + $leftover = array_merge($leftover,$numbers); + if ($punctuation > 0) + $leftover = array_merge($leftover,$punc); + + if (count($leftover) == 0) + $leftover = array_merge($leftover,$llower,$lupper,$numbers,$punc); + + shuffle($leftover); + $outarray = array_merge($outarray,a_array_rand($leftover,$length-$num_spec)); + } + + shuffle($outarray); + $return = implode('',$outarray); + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; +} + +/** + * This function returns an array of $num_req values + * randomly picked from the $input array + * + * @param array Array of values + * @param integer Number of values in returned array + * @return string The padded string + */ +function a_array_rand($input,$num_req) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (count($input) == 0) + return array(); + + if ($num_req < 1) + return array(); + + $return = array(); + if ($num_req > count($input)) { + for($i = 0; $i < $num_req; $i++) { + $idx = array_rand($input,1); + $return[] = $input[$idx]; + } + + } else { + $idxlist = array_rand($input,$num_req); + if ($num_req == 1) + $idxlist = array($idxlist); + + for($i = 0; $i < count($idxlist); $i++) + $return[] = $input[$idxlist[$i]]; + } + + if (DEBUG_ENABLED) + debug_log('Returning (%s)',1,0,__FILE__,__LINE__,__METHOD__,$return); + + return $return; +} + +/** + * This is for Opera. By putting "random junk" in the query string, it thinks + * that it does not have a cached version of the page, and will thus + * fetch the page rather than display the cached version + */ +function random_junk() { + $time = gettimeofday(); + return md5(strtotime('now').$time['usec']); +} + +/** + * Returns a HTML id that can be used in the URL after the #. + * + * @param string The DN to pretty-print. + * @return string + */ +function htmlid($sid,$dn) { + return sprintf('SID%s:%s',$sid,preg_replace('/[\ =,]/','_',$dn)); +} + +/** + * Is PLA configured for AJAX display + */ +function isAjaxEnabled() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',1,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($_SESSION[APPCONFIG])) + return ($_SESSION[APPCONFIG]->getValue('appearance','tree') == 'AJAXTree'); + else + return false; +} +?> diff --git a/lam/templates/3rdParty/pla/lib/hooks.php b/lam/templates/3rdParty/pla/lib/hooks.php new file mode 100644 index 00000000..57066ed1 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/hooks.php @@ -0,0 +1,201 @@ + and AlcĂ?ve + * @package phpLDAPadmin + */ + +/** + * Compares two arrays by numerically comparing their 'prority' + * value. Standard `cmp-like' function. + * + * @param a First element to compare. + * @param b Second element to compare. + * + * @return -1 if priority of first element is smaller than second + * element priority. 1 otherwise. + */ +function sort_array_by_priority($a,$b) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',257,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return (($a['priority'] < $b['priority']) ? -1 : 1 ); +} + +/** + * Runs procedures attached to a hook. + * + * @param hook_name Name of hook to run. + * @param args Array of optional arguments set by phpldapadmin. It is normally in a form known by call_user_func_array() : + * + *
[ 'server_id' => 0,
+ * 'dn' => 'uid=epoussa,ou=tech,o=corp,o=fr' ]
+ * + * @return true if all procedures returned true, false otherwise. + */ +function run_hook($hook_name,$args) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',257,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $hooks = isset($_SESSION[APPCONFIG]) ? $_SESSION[APPCONFIG]->hooks : array(); + + if (! count($hooks) || ! array_key_exists($hook_name,$hooks)) { + if (DEBUG_ENABLED) + debug_log('Returning, HOOK not defined (%s)',257,0,__FILE__,__LINE__,__METHOD__,$hook_name); + + return true; + } + + $rollbacks = array(); + reset($hooks[$hook_name]); + + /* Execution of procedures attached is done using a numeric order + * since all procedures have been attached to the hook with a + * numerical weight. */ + while (list($key,$hook) = each($hooks[$hook_name])) { + if (DEBUG_ENABLED) + debug_log('Calling HOOK Function (%s)(%s)',257,0,__FILE__,__LINE__,__METHOD__, + $hook['hook_function'],$args); + + array_push($rollbacks,$hook['rollback_function']); + + $result = call_user_func_array($hook['hook_function'],$args); + if (DEBUG_ENABLED) + debug_log('Called HOOK Function (%s)',257,0,__FILE__,__LINE__,__METHOD__, + $hook['hook_function']); + + /* If a procedure fails (identified by a false return), its optional rollback is executed with + * the same arguments. After that, all rollbacks from + * previously executed procedures are executed in the reverse + * order. */ + if (! is_null($result) && $result == false) { + if (DEBUG_ENABLED) + debug_log('HOOK Function [%s] return (%s)',257,0,__FILE__,__LINE__,__METHOD__, + $hook['hook_function'],$result); + + while ($rollbacks) { + $rollback = array_pop($rollbacks); + + if ($rollback != false) { + if (DEBUG_ENABLED) + debug_log('HOOK Function Rollback (%s)',257,0,__FILE__,__LINE__,__METHOD__, + $rollback); + + call_user_func_array($rollback,$args); + } + } + + return false; + } + } + + return true; +} + +/** + * Adds a procedure to a hook for later execution. + * + * @param hook_name Name of the hook. + * @param hook_function Name of the php function called upon hook trigger. + * @param priority Numeric priority. Lowest means procedure will be executed before. + * @param rollback_function Name of the php rollback function called upon failure. + */ +function add_hook($hook_name,$hook_function,$priority=0,$rollback_function=null) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',257,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # First, see if the hook function exists. + if (! function_exists($hook_function)) { + system_message(array( + 'title'=>_('Hook function does not exist'), + 'body'=>sprintf('Hook name: %s
Hook function: %s',$hook_name,$hook_function), + 'type'=>'warn')); + + return; + } + + if (! array_key_exists($hook_name,$_SESSION[APPCONFIG]->hooks)) + $_SESSION[APPCONFIG]->hooks[$hook_name] = array(); + + remove_hook($hook_name,$hook_function,-1,null); + + array_push($_SESSION[APPCONFIG]->hooks[$hook_name],array( + 'priority' => $priority, + 'hook_function' => $hook_function, + 'rollback_function' => $rollback_function)); + + uasort($_SESSION[APPCONFIG]->hooks[$hook_name],'sort_array_by_priority'); +} + +/** + * Removes a procedure from a hook, based on a filter. + * + * @param hook_name Name of the hook. + * @param priority Numeric priority. If set, all procedures of that priority will be removed. + * @param hook_function Name of the procedure function. If set, all procedures that call this function will be removed. + * @param rollback_function Name of the php rollback function called upon failure. If set, all + * procedures that call this function as a rollback will be removed. + */ +function remove_hook($hook_name,$hook_function,$priority,$rollback_function) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',257,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (array_key_exists($hook_name,$_SESSION[APPCONFIG]->hooks)) { + reset($_SESSION[APPCONFIG]->hooks[$hook_name]); + + while (list($key,$hook) = each($_SESSION[APPCONFIG]->hooks[$hook_name])) { + if (($priority >= 0 && $priority == $hook['priority']) || + ($hook_function && $hook_function == $hook['hook_function']) || + ($rollback_function && $rollback_function == $hook['rollback_function'])) { + + unset($_SESSION[APPCONFIG]->hooks[$hook_name][$key]); + } + } + } +} + +/** + * Removes all procedures from a hook. + * + * @param hook_name Name of hook to clear. + */ +function clear_hooks($hook_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',257,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (array_key_exists($hook_name,$_SESSION[APPCONFIG]->hooks)) + unset($_SESSION[APPCONFIG]->hooks[$hook_name]); +} + +$hooks = array(); + +# Evaluating user-made hooks +if (is_dir(HOOKSDIR.'functions')) { + $hooks['dir'] = dir(HOOKSDIR.'functions'); + + while ($hooks['file'] = $hooks['dir']->read()) { + $script = sprintf('%s/%s/%s',HOOKSDIR,'functions',$hooks['file']); + + if (is_file($script) && preg_match('/php[0-9]?$/',$hooks['file'])) + require_once $script; + } + + $hooks['dir']->close(); +} +?> diff --git a/lam/templates/3rdParty/pla/lib/import_functions.php b/lam/templates/3rdParty/pla/lib/import_functions.php new file mode 100644 index 00000000..8ef40bed --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/import_functions.php @@ -0,0 +1,609 @@ +server_id = $server_id; + $this->template_id = $template_id; + + $this->accept(); + } + + static function types() { + $type = array(); + + $details = ImportLDIF::getType(); + $type[$details['type']] = $details; + + return $type; + } + + private function accept() { + switch($this->template_id) { + case 'LDIF': + $this->template = new ImportLDIF($this->server_id); + break; + + default: + system_message(array( + 'title'=>sprintf('%s %s',_('Unknown Import Type'),$this->template_id), + 'body'=>_('phpLDAPadmin has not been configured for that import type'), + 'type'=>'warn'),'index.php'); + + die(); + } + + $this->template->accept(); + } + + public function getTemplate() { + return $this->template; + } +} + +/** + * Import Class + * + * This abstract classes provides all the common methods and variables for the + * custom import classes. + * + * @package phpLDAPadmin + * @subpackage Import + */ +abstract class Import { + protected $server_id = null; + protected $input = null; + protected $source = array(); + + public function __construct($server_id) { + $this->server_id = $server_id; + } + + public function accept() { + if (get_request('ldif','REQUEST')) { + $this->input = explode("\n",get_request('ldif','REQUEST')); + $this->source['name'] = 'STDIN'; + $this->source['size'] = strlen(get_request('ldif','REQUEST')); + + } elseif (isset($_FILES['ldif_file']) && is_array($_FILES['ldif_file']) && ! $_FILES['ldif_file']['error']) { + $input = file_get_contents($_FILES['ldif_file']['tmp_name']); + $this->input = preg_split("/\n|\r\n|\r/",$input); + $this->source['name'] = $_FILES['ldif_file']['name']; + $this->source['size'] = $_FILES['ldif_file']['size']; + + } else { + system_message(array( + 'title'=>_('No import input'), + 'body'=>_('You must either upload a file or provide an import in the text box.'), + 'type'=>'error'),sprintf('cmd.php?cmd=import_form&server_id=%s',get_request('server_id','REQUEST'))); + + die(); + } + } + + public function getSource($attr) { + if (isset($this->source[$attr])) + return $this->source[$attr]; + else + return null; + } + + # @todo integrate hooks + public function LDAPimport() { + $template = $this->getTemplate(); + $server = $this->getServer(); + + switch ($template->getType()) { + case 'add': + return $server->add($template->getDN(),$template->getLDAPadd()); + + case 'modify': + return $server->modify($template->getDN(),$template->getLDAPmodify()); + + case 'moddn': + case 'modrdn': + return $server->rename($template->getDN(),$template->modrdn['newrdn'],$template->modrdn['newsuperior'],$template->modrdn['deleteoldrdn']); + + default: + debug_dump_backtrace(sprintf('Unknown template type %s',$template->getType()),1); + } + + return true; + } +} + +/** + * Import entries from LDIF + * + * The LDIF spec is described by RFC2849 + * http://www.ietf.org/rfc/rfc2849.txt + * + * @package phpLDAPadmin + * @subpackage Import + */ +class ImportLDIF extends Import { + private $_currentLineNumber = 0; + private $_currentLine = ''; + private $template; + public $error = array(); + + static public function getType() { + return array('type'=>'LDIF','description' => _('LDIF Import'),'extension'=>'ldif'); + } + + protected function getTemplate() { + return $this->template; + } + + protected function getServer() { + return $_SESSION[APPCONFIG]->getServer($this->server_id); + } + + public function readEntry() { + static $haveVersion = false; + + if ($lines = $this->nextLines()) { + + # If we have a version line. + if (! $haveVersion && preg_match('/^version:/',$lines[0])) { + list($text,$version) = $this->getAttrValue(array_shift($lines)); + + if ($version != 1) + return $this->error(sprintf('%s %s',_('LDIF import only suppports version 1'),$version),$lines); + + $haveVersion = true; + $lines = $this->nextLines(); + } + + $server = $this->getServer(); + + # The first line should be the DN + if (preg_match('/^dn:/',$lines[0])) { + list($text,$dn) = $this->getAttrValue(array_shift($lines)); + + # The second line should be our changetype + if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) { + $attrvalue = $this->getAttrValue($lines[0]); + $changetype = $attrvalue[1]; + array_shift($lines); + + } else + $changetype = 'add'; + + $this->template = new Template($this->server_id,null,null,$changetype); + + switch ($changetype) { + case 'add': + $rdn = get_rdn($dn); + $container = $server->getContainer($dn); + + $this->template->setContainer($container); + $this->template->accept(); + + $this->getAddDetails($lines); + $this->template->setRDNAttributes($rdn); + + return $this->template; + + break; + + case 'modify': + if (! $server->dnExists($dn)) + return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines); + + $this->template->setDN($dn); + $this->template->accept(); + + return $this->getModifyDetails($lines); + + break; + + case 'moddn': + case 'modrdn': + if (! $server->dnExists($dn)) + return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines); + + $this->template->setDN($dn); + $this->template->accept(); + + return $this->getModRDNAttributes($lines); + + break; + + default: + if (! $server->dnExists($dn)) + return $this->error(_('Unkown change type'),$lines); + } + + } else + return $this->error(_('A valid dn line is required'),$lines); + + } else + return false; + } + + /** + * Get the Attribute and Decoded Value + */ + private function getAttrValue($line) { + list($attr,$value) = explode(':',$line,2); + + # Get the DN + if (substr($value,0,1) == ':') + $value = base64_decode(trim(substr($value,1))); + else + $value = trim($value); + + return array($attr,$value); + } + + /** + * Get the lines of the next entry + * + * @return The lines (unfolded) of the next entry + */ + private function nextLines() { + $current = array(); + $endEntryFound = false; + + if ($this->hasMoreEntries() && ! $this->eof()) { + # The first line is the DN one + $current[0]= trim($this->_currentLine); + + # While we end on a blank line, fetch the attribute lines + $count = 0; + while (! $this->eof() && ! $endEntryFound) { + # Fetch the next line + $this->nextLine(); + + /* If the next line begin with a space, we append it to the current row + * else we push it into the array (unwrap)*/ + if ($this->isWrappedLine()) + $current[$count] .= trim($this->_currentLine); + elseif ($this->isCommentLine()) {} + # Do nothing + elseif (! $this->isBlankLine()) + $current[++$count] = trim($this->_currentLine); + else + $endEntryFound = true; + } + + # Return the LDIF entry array + return $current; + + } else + return array(); + } + + /** + * Private method to check if there is more entries in the input. + * + * @return boolean true if an entry was found, false otherwise. + */ + private function hasMoreEntries() { + $entry_found = false; + + while (! $this->eof() && ! $entry_found) { + # If it's a comment or blank line, switch to the next line + if ($this->isCommentLine() || $this->isBlankLine()) { + # Do nothing + $this->nextLine(); + + } else { + $this->_currentDnLine = $this->_currentLine; + $this->dnLineNumber = $this->_currentLineNumber; + $entry_found = true; + } + } + + return $entry_found; + } + + /** + * Helper method to switch to the next line + */ + private function nextLine() { + $this->_currentLineNumber++; + $this->_currentLine = array_shift($this->input); + } + + /** + * Check if it's a comment line. + * + * @return boolean true if it's a comment line,false otherwise + */ + private function isCommentLine() { + return substr(trim($this->_currentLine),0,1) == '#' ? true : false; + } + + /** + * Check if it's a wrapped line. + * + * @return boolean true if it's a wrapped line,false otherwise + */ + private function isWrappedLine() { + return substr($this->_currentLine,0,1) == ' ' ? true : false; + } + + /** + * Check if is the current line is a blank line. + * + * @return boolean if it is a blank line,false otherwise. + */ + private function isBlankLine() { + return(trim($this->_currentLine) == '') ? true : false; + } + + /** + * Returns true if we reached the end of the input. + * + * @return boolean true if it's the end of file, false otherwise. + */ + public function eof() { + return count($this->input) > 0 ? false : true; + } + + private function error($msg,$data) { + $this->error['message'] = sprintf('%s [%s]',$msg,$this->template ? $this->template->getDN() : ''); + $this->error['line'] = $this->_currentLineNumber; + $this->error['data'] = $data; + $this->error['changetype'] = $this->template ? $this->template->getType() : 'Not set'; + + return false; + } + + /** + * Method to retrieve the attribute value of a ldif line, + * and get the base 64 decoded value if it is encoded + */ + private function getAttributeValue($value) { + $return = ''; + + if (substr($value,0,1) == '<') { + $url = trim(substr($value,1)); + + if (preg_match('^file://',$url)) { + $filename = substr(trim($url),7); + + if ($fh = @fopen($filename,'rb')) { + if (! $return = @fread($fh,filesize($filename))) + return $this->error(_('Unable to read file for'),$value); + + @fclose($fh); + + } else + return $this->error(_('Unable to open file for'),$value); + + } else + return $this->error(_('The url attribute value should begin with file:// for'),$value); + + # It's a string + } else + $return = $value; + + return trim($return); + } + + /** + * Build the attributes array when the change type is add. + */ + private function getAddDetails($lines) { + foreach ($lines as $line) { + list($attr,$value) = $this->getAttrValue($line); + + if (is_null($attribute = $this->template->getAttribute($attr))) { + $attribute = $this->template->addAttribute($attr,array('values'=>array($value))); + $attribute->justModified(); + + } else + if ($attribute->hasBeenModified()) + $attribute->addValue($value); + else + $attribute->setValue(array($value)); + } + } + + /** + * Build the attributes array for the entry when the change type is modify + */ + private function getModifyDetails($lines) { + if (! count($lines)) + return $this->error(_('Missing attributes for'),$lines); + + # While the array is not empty + while (count($lines)) { + $processline = false; + $deleteattr = false; + + # Get the current line with the action + $currentLine = array_shift($lines); + $attrvalue = $this->getAttrValue($currentLine); + $action_attribute = $attrvalue[0]; + $action_attribute_value = $attrvalue[1]; + + if (! in_array($action_attribute,array('add','delete','replace'))) + return $this->error(_('Missing modify command add, delete or replace'),array_merge(array($currentLine),$lines)); + + $processline = true; + switch ($action_attribute) { + case 'add': + + break; + + case 'delete': + $attribute = $this->template->getAttribute($action_attribute_value); + + if (is_null($attribute)) + return $this->error(sprintf('%s %s',_('Attempting to delete a non existant attribute'),$action_attribute_value), + array_merge(array($currentLine),$lines)); + + $deleteattr = true; + + break; + + case 'replace': + $attribute = $this->template->getAttribute($action_attribute_value); + + if (is_null($attribute)) + return $this->error(sprintf('%s %s',_('Attempting to replace a non existant attribute'),$action_attribute_value), + array_merge(array($currentLine),$lines)); + + break; + + default: + debug_dump_backtrace(sprintf('Unknown action %s',$action_attribute),1); + } + + # Fetch the attribute for the following line + $currentLine = array_shift($lines); + + while ($processline && trim($currentLine) && (trim($currentLine) != '-')) { + $processline = false; + + # If there is a valid line + if (preg_match('/:/',$currentLine)) { + $attrvalue = $this->getAttrValue($currentLine); + $attr = $attrvalue[0]; + $attribute_value_part = $attrvalue[1]; + + # Check that it correspond to the one specified before + if ($attr == $action_attribute_value) { + # Get the value part of the attribute + $attribute_value = $this->getAttributeValue($attribute_value_part); + + $attribute = $this->template->getAttribute($attr); + + # This should be a add/replace operation + switch ($action_attribute) { + case 'add': + if (is_null($attribute)) + $attribute = $this->template->addAttribute($attr,array('values'=>array($attribute_value_part))); + else + $attribute->addValue($attribute_value_part,-1); + + $attribute->justModified(); + + break; + + case 'delete': + $deleteattr = false; + + if ($key = array_search($attribute_value_part,$attribute->getValues())) + $attribute->delValue($key); + else + return $this->error(sprintf('%s %s',_('Delete value doesnt exist in DN'),$attribute_value_part), + array_merge(array($currentLine),$lines)); + + + break; + + case 'replace': + if ($attribute->hasBeenModified()) + $attribute->addValue($attribute_value_part,-1); + else + $attribute->setValue(array($attribute_value_part)); + + break; + + default: + debug_dump_backtrace(sprintf('Unexpected operation %s',$action_attribute)); + } + + } else + return $this->error(sprintf('%s %s',_('The attribute to modify doesnt match the one specified by'),$action_attribute), + array_merge(array($currentLine),$lines)); + + } else + return $this->error(sprintf('%s %s',_('Attribute not valid'),$currentLine), + array_merge(array($currentLine),$lines)); + + $currentLine = array_shift($lines); + if (trim($currentLine)) + $processline = true; + } + + if ($action_attribute == 'delete' && $deleteattr) + $attribute->setValue(array()); + + } + + return $this->template; + } + + /** + * Build the attributes for the entry when the change type is modrdn + */ + function getModRDNAttributes($lines) { + $server = $this->getServer(); + $attrs = array(); + + # MODRDN MODDN should only be 2 or 3 lines. + if (count($lines) != 2 && count($lines) !=3) + return $this->error(_('Invalid entry'),$lines); + + else { + $currentLine = array_shift($lines); + + # First we need to check if there is an new rdn specified + if (preg_match('/^newrdn:(:?)/',$currentLine)) { + + $attrvalue = $this->getAttrValue($currentLine); + $attrs['newrdn'] = $attrvalue[1]; + + $currentLine = array_shift($lines); + + if (preg_match('/^deleteoldrdn:[ ]*(0|1)/',$currentLine)) { + $attrvalue = $this->getAttrValue($currentLine); + $attrs['deleteoldrdn'] = $attrvalue[1]; + + # Switch to the possible new superior attribute + if (count($lines)) { + $currentLine = array_shift($lines); + + # then the possible new superior attribute + if (preg_match('/^newsuperior:/',$currentLine)) { + $attrvalue = $this->getAttrValue($currentLine); + $attrs['newsuperior'] = $attrvalue[1]; + + } else + return $this->error(_('A valid newsuperier attribute should be specified'),$lines); + + } else + $attrs['newsuperior'] = $server->getContainer($this->template->getDN()); + + } else + return $this->error(_('A valid deleteoldrdn attribute should be specified'),$lines); + + } else + return $this->error(_('A valid newrdn attribute should be specified'),$lines); + } + + # Well do something out of the ordinary here, since our template doesnt handle mod[r]dn yet. + $this->template->modrdn = $attrs; + return $this->template; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/ldap_error_codes.txt b/lam/templates/3rdParty/pla/lib/ldap_error_codes.txt new file mode 100644 index 00000000..8d4a2cce --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/ldap_error_codes.txt @@ -0,0 +1,94 @@ +0x00 LDAP_SUCCESS "The operation completed successfully." +0x01 LDAP_OPERATIONS_ERROR "An operations error occurred. This is + typically the result of an internal error on your LDAP server." +0x02 LDAP_PROTOCOL_ERROR "A protocol violation was detected." +0x03 LDAP_TIMELIMIT_EXCEEDED "The operation timed out waiting to complete." +0x04 LDAP_SIZELIMIT_EXCEEDED "The LDAP server refused to serve such a large result set." +0x05 LDAP_COMPARE_FALSE "A compare operation returned false." +0x06 LDAP_COMPARE_TRUE "A compare operation returned true." +0x07 LDAP_AUTH_METHOD_NOT_SUPPORTED "The authentication method you specified is not supported by + the LDAP server." +0x08 LDAP_STRONG_AUTH_REQUIRED "This LDAP server requires strong (encrypted) authentication, + not clear text." +0x09 LDAP_PARTIAL_RESULTS "The result set received is a partial result set." +0x0a LDAP_REFERRAL "" +0x0b LDAP_ADMINLIMIT_EXCEEDED "" +0x0c LDAP_UNAVAILABLE_CRITICAL_EXTENSION "" +0x0d LDAP_CONFIDENTIALITY_REQUIRED "" +0x0e LDAP_SASL_BIND_INPROGRESS "" +0x10 LDAP_NO_SUCH_ATTRIBUTE "That entry does not contain the attribute specified." +0x11 LDAP_UNDEFINED_TYPE "The attribute type specified is invalid." +0x12 LDAP_INAPPROPRIATE_MATCHING "This usually means that your LDAP server has not defined an equality rule + for the attribute you are trying to alter. This is not phpLDAPadmin's fault as the + LDAP server has refused to perform the operation (as well it should if there is + no equality rule for it to use for the operation). This generally applies when + adding a new value to a binary attribute, or removing a single value from a + multi-valued binary attribute." +0x13 LDAP_CONSTRAINT_VIOLATION "Some constraint would be violated by performing the action. This can happen when + you try to add a second value to a single-valued attribute, for example." +0x14 LDAP_TYPE_OR_VALUE_EXISTS "An attribute type or attribute value + specified already exists in the entry" +0x15 LDAP_INVALID_SYNTAX "An invalid attribute value was specified." +0x20 LDAP_NO_SUCH_OBJECT "That object does not exist." +0x21 LDAP_ALIAS_PROBLEM "An alias in the directory points to a + non-existent entry." +0x22 LDAP_INVALID_DN_SYNTAX "You used an invalid syntax in the specified DN." +0x23 LDAP_IS_LEAF "The object specified is a leaf" +0x24 LDAP_ALIAS_DEREF_PROBLEM "A problem was encountereed when + dereferencing an alias" +0x30 LDAP_INAPPROPRIATE_AUTH "Inappropriate authentication was + specified (e.g. LDAP_AUTH_SIMPLE was + specified and the entry does not have + a userPassword attribute)." +0x31 LDAP_INVALID_CREDENTIALS "Incorrect login DN and/or password." +0x32 LDAP_INSUFFICIENT_ACCESS "You do not have sufficient permissions + to perform that operation." +0x33 LDAP_BUSY "The LDAP server is busy." +0x34 LDAP_UNAVAILABLE "The LDAP server is unavailable." +0x35 LDAP_UNWILLING_TO_PERFORM "The LDAP server refused to perform the operation." +0x36 LDAP_LOOP_DETECT "A loop was detected." +0x3C LDAP_SORT_CONTROL_MISSING "" +0x3D LDAP_INDEX_RANGE_ERROR "" +0x40 LDAP_NAMING_VIOLATION "A naming violation occurred. This usually + means that you tried to change the value of an attribute that is used in the + DN. For example, if you change the 'cn' value of an entry whose DN is 'cn=Bob + Jones,dc=example,dc=com', you must also rename the entry to reflect the + change." +0x41 LDAP_OBJECT_CLASS_VIOLATION "You tried to perform an operation that would cause an undefined attribute + to exist or that would remove a required attribute, given the current list + of ObjectClasses. This can also occur if you do not + specify a structural objectClass when creating an entry, or if you specify + more than one structural objectClass." +0x42 LDAP_NOT_ALLOWED_ON_NONLEAF "The entry you tried to operate on has children. Usually this means you + tried to delete or rename the entry, which you cannot do to an entry + with children." +0x43 LDAP_NOT_ALLOWED_ON_RDN "You cannot preform that operation on a the relative distinguished name + (RDN) of an object." +0x44 LDAP_ALREADY_EXISTS "The object already exists. Usually you are trying to create a new object + on top of an existing one." +0x45 LDAP_NO_OBJECT_CLASS_MODS "ObjectClass modifications are not allowed." +0x46 LDAP_RESULTS_TOO_LARGE "" +0x47 LDAP_AFFECTS_MULTIPLE_DSAS "" +0x50 LDAP_OTHER "" +0x51 LDAP_SERVER_DOWN "The LDAP server is down." +0x52 LDAP_LOCAL_ERROR "" +0x53 LDAP_ENCODING_ERROR "" +0x54 LDAP_DECODING_ERROR "" +0x55 LDAP_TIMEOUT "" +0x56 LDAP_AUTH_UNKNOWN "" +0x57 LDAP_FILTER_ERROR "The LDAP search filter specified is inavlid." +0x58 LDAP_USER_CANCELLED "The user cancelled the LDAP operation." +0x59 LDAP_PARAM_ERROR "An ldap routine was called with a bad + parameter." +0x5a LDAP_NO_MEMORY "A memory allocation (e.g., malloc(3) + or other dynamic memory allocator) + call failed in an ldap library rou- + tine." +0x5b LDAP_CONNECT_ERROR "" +0x5c LDAP_NOT_SUPPORTED "The requested operation is not supported by the LDAP server." +0x5d LDAP_CONTROL_NOT_FOUND "" +0x5e LDAP_NO_RESULTS_RETURNED "The search came back empty." +0x5f LDAP_MORE_RESULTS_TO_RETURN "The LDAP server has more results that it would like to return." +0x60 LDAP_CLIENT_LOOP "" +0x61 LDAP_REFERRAL_LIMIT_EXCEEDED "This means that a search was performed that required the LDAP + server to follow a chain of referrals that was too lengthy." diff --git a/lam/templates/3rdParty/pla/lib/ldap_supported_oids.txt b/lam/templates/3rdParty/pla/lib/ldap_supported_oids.txt new file mode 100644 index 00000000..9fc5a661 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/ldap_supported_oids.txt @@ -0,0 +1,187 @@ +# If you find some reliable and more meaningful descriptions to this OIDS, +# then please let the phpldapadmin development know so that this file can be +# more descriptive. + +1.2.826.0.1.334810.2.3 "LDAP_CONTROL_VALUESRETURNFILTER" +1.2.826.0.1.3344810.2.3 "Matched Values Control" "RFC 3876" "Describes a control for the LDAP v3 that is used to return a subset of attribute values from an entry. Specifically, only those values that match a 'values return' filter. Without support for this control, a client must retrieve all of an attribute's values and search for specific values locally." +1.2.826.0.1050.11.1.1 "Read-Only LDAP Server" +1.2.826.0.1050.11.2.1 "Read-Write LDAP Server" +1.2.826.0.1050.11.3.1 "White Pages Application LDAP Server" +1.2.826.0.1050.11.4.1 "Certificate Application LDAP Server" +1.2.826.0.1050.11.5.1 "Single Sign On Application LDAP Server" +1.2.840.113549.6.0.0 "Signed Operation" +1.2.840.113549.6.0.1 "Demand Signed Result" +1.2.840.113549.6.0.2 "Signed Result RFC 2649" +1.2.840.113556.1.4.319 "Simple Paged Results Manipulation Control Extension" "RFC 2696" "This control extension allows a client to control the rate at which an LDAP server returns the results of an LDAP search operation. This control may be useful when the LDAP client has limited resources and may not be able to process the entire result set from a given LDAP query, or when the LDAP client is connected over a low-bandwidth connection." +1.2.840.113556.1.4.417 "Show deleted control" "" "The LDAP_SERVER_SHOW_DELETED_OID control is used with an extended LDAP search function to specify that the search results include any deleted objects that match the search filter." +1.2.840.113556.1.4.473 "LDAP Server Sort Result extension" "draft-ietf-ldapext-sorting-01" "This control is included in the searchRequest message as part of the controls field of the LDAPMessage." +1.2.840.113556.1.4.474 "LDAP Server Sort Result extension response control" "" "This control is included in the searchResultDone message as part of the controls field of the LDAPMessage" +1.2.840.113556.1.4.521 "Cross-domain move control" "" "The LDAP_SERVER_CROSSDOM_MOVE_TARGET_OID control is used with an extended LDAP rename function to move an LDAP object from one domain to another. The control specifies the DNS hostname of the domain controller in the destination domain." +1.2.840.113556.1.4.528 "Server search notification control" "" "The LDAP_SERVER_NOTIFICATION_OID control is used with an extended LDAP asynchronous search function to register the client to be notified when changes are made to an object in Active Directory." +1.2.840.113556.1.4.529 "Extended DN control" "" "The LDAP_SERVER_EXTENDED_DN_OID control is used with an extended LDAP search function to request an extended form of an Active Directory object distinguished name. The extended form includes a string representation of the object objectGUID property. For security principal objects such as users, groups, and computers, the extended form also includes a string representation of the object objectSID property." +1.2.840.113556.1.4.616 "LDAP_CONTROL_REFERRALS" +1.2.840.113556.1.4.619 "Lazy commit control" "" "The LDAP_SERVER_LAZY_COMMIT_OID control is used to instruct the server to return the results of a DS modification command, such as add, delete, or replace, after it has been completed in memory, but before it has been committed to disk. The server can then return results quickly, and save the data to disk without holding the client." +1.2.840.113556.1.4.800 "LDAP_CAP_ACTIVE_DIRECTORY_OID" "" "This is an Actrive Directory Server (Win2k and later)." +1.2.840.113556.1.4.801 "Security descriptor flags control" "" "The LDAP_SERVER_SD_FLAGS_OID control is used to pass flags to the server to control various security descriptor results." +1.2.840.113556.1.4.802 "Attribute Range Option" "" "Server supports the Range property enabling clients to incremental retrieve values from multivalue attributes." +1.2.840.113556.1.4.803 "LDAP_MATCHING_RULE_BIT_AND" +1.2.840.113556.1.4.804 "LDAP_MATCHING_RULE_BIT_OR" +1.2.840.113556.1.4.805 "Tree Delete" "" "The LDAP_SERVER_TREE_DELETE_OID control is used with an extended LDAP delete function to delete an entire subtree in the directory." +1.2.840.113556.1.4.841 "Directory synchronization control" "" "The LDAP_SERVER_DIRSYNC_OID control enables an application to search the directory for objects changed from a previous state. It is also used with the extended LDAP search functions such as ldap_search_ext." +1.2.840.113556.1.4.906 "Microsoft Large Integer" +1.2.840.113556.1.4.970 "Get stats control (Stateless)" +1.2.840.113556.1.4.1302 "Microsoft OID used with DEN Attributes" +1.2.840.113556.1.4.1338 "Verify name control" "" "The LDAP_SERVER_VERIFY_NAME_OID control is used with extended LDAP add and modify requests to instruct the DC accepting the update which DC it should verify with, the existence of any DN attribute values." +1.2.840.113556.1.4.1339 "LDAP_SERVER_DOMAIN_SCOPE_OID" "" "The LDAP_SERVER_DOMAIN_SCOPE_OID control is used to instruct the LDAP server not to generate any referrals when completing a request. This control also limits any search using it to a single naming context." +1.2.840.113556.1.4.1340 "Search options control" "" " The LDAP_SERVER_SEARCH_OPTIONS_OID control is used to pass flags to the server to control various search behaviors." +1.2.840.113556.1.4.1413 "LDAP ease modify restrictions" "" "Allows an LDAP modify to work under less restrictive conditions. Without it, a delete will fail if an attribute does not exist, and an add will fail if an attribute already exists." +1.2.840.113556.1.4.1504 "Attribute scoped query control" "" "The LDAP_SERVER_ASQ_OID control is used with an extended LDAP search function to force the query to be based on a specific DN-valued attribute. Only one source attribute can be specified with this control and the search request is limited to base object scoped queries." +1.2.840.113556.1.4.1670 "LDAP_CAP_ACTIVE_DIRECTORY_V51_OID" "" "This server is a Whistler Active Directory server (Win2k3 and later)." +1.2.840.113556.1.4.1781 "Fast concurrent bind extended operation" "" "The Microsoft LDAP API will send an extended request with this name to Active Directory to request that all binds on this connection be processed as 'fast' binds." +1.2.840.113556.1.4.1791 "LDAP_CAP_ACTIVE_DIRECTORY_LDAP_INTEG_OID" "" "LDAP server is capable of doing signing and sealing on an NTLM authenticated connection, and that the server is capable of performing subsequent binds on a signed or sealed connection." +1.2.840.113556.1.4.1852 "LDAP_SERVER_QUOTA_CONTROL_OID" "" "The LDAP_SERVER_QUOTA_CONTROL_OID control is used to pass the SID of a security principal, whose quota is being queried, to the server in a LDAP search operation." +1.3.6.1.1.7.1 "LCUP Sync Request Control. RFC 3928 control" +1.3.6.1.1.7.2 "LCUP Sync Update Control. RFC 3928 control" +1.3.6.1.1.7.3 "LCUP Sync Done Control. RFC 3928 control" +1.3.6.1.1.8 "Cancel Operation. RFC 3909 extension" +1.3.6.1.1.12 "Assertion Control" "RFC 4511" "The assertion control allows the client to specify a condition that must be true for the operation to be processed normally." +1.3.6.1.1.13.1 "Pre-Read Controls" "" "The Pre-Read request control, indicates that a copy of the entry before application of update is to be returned." +1.3.6.1.1.13.2 "Post-Read Controls" "" "The Pre-Read request control, indicates that a copy of the entry before application of update is to be returned." +1.3.6.1.1.14 "Modify-Increment Extension" "RFC 4525" "An extension to the Lightweight Directory Access Protocol (LDAP) Modify operation to support an increment capability." +1.3.6.1.4.1.42.2.27.8.5.1 "passwordPolicyRequest" +1.3.6.1.4.1.42.2.27.9.5.2 "GetEffectiveRights control" "" "May be used to determine what operations a given user may perform on a specified entry." +1.3.6.1.4.1.1466.101.119.1 "Dynamic Directory Services Refresh Request" "RFC 2589" +1.3.6.1.4.1.1466.20036 "LDAP_NOTICE_OF_DISCONNECTION" +1.3.6.1.4.1.1466.20037 "Transport Layer Security Extension" "RFC 2830" "This operation provides for TLS establishment in an LDAP association and is defined in terms of an LDAP extended request." +1.3.6.1.4.1.1466.29539.1 "LDAP_CONTROL_ATTR_SIZELIMIT" +1.3.6.1.4.1.1466.29539.2 "LDAP_CONTROL_NO_COPY" +1.3.6.1.4.1.1466.29539.3 "LDAP_CONTROL_PARTIAL_COPY" +1.3.6.1.4.1.1466.29539.5 "LDAP_CONTROL_NO_CHAINING" +1.3.6.1.4.1.1466.29539.7 "LDAP_CONTROL_ALIAS_ON_UPDATE" +1.3.6.1.4.1.1466.29539.10 "LDAP_CONTROL_TRIGGER" +1.3.6.1.4.1.1466.29539.12 "nsTransmittedControl" +1.3.6.1.4.1.4203.1.5.1 "All Operational Attribute" "RFC 3673" "An LDAP extension which clients may use to request the return of all operational attributes." +1.3.6.1.4.1.4203.1.5.2 "Requesting Attributes by Object Class" "draft-zeilenga-ldap-adlist-10.txt" "Extends LDAP to support a mechanism that LDAP clients may use to request the return of all attributes of an object class." +1.3.6.1.4.1.4203.1.5.3 "LDAP Absolute True and False Filters" "draft-zeilenga-ldap-t-f-10.txt" "Implementations of this extension SHALL allow 'and' and 'or' choices with zero filter elements." +1.3.6.1.4.1.4203.1.5.4 "Language Tags" "RFC 3866" "Supports storing attributes with language tag options in the DIT" +1.3.6.1.4.1.4203.1.5.5 "Language Ranges" "RFC 3866" "Supports language range matching of attributes with language tag options stored in the DIT" +1.3.6.1.4.1.4203.1.9.1.1 "LDAP Content Synchronization Control" "draft=zeilenga-ldup-sync-06.txt" "The operation allows a client to maintain a copy of a fragment of directory information tree. It supports both polling for changes and listening for changes. The operation is defined as an extension of the LDAP Search Operation." +1.3.6.1.4.1.4203.1.10.1 "Subentries in LDAP" "RFC 3672" "The subentries control MAY be sent with a searchRequest to control the visibility of entries and subentries which are within scope. Non-visible entries or subentries are not returned in response to the request." +1.3.6.1.4.1.4203.1.10.2 "LDAP No-Op Control" "draft-zeilenga-ldap-noop-02.txt" "The No-Op control can be used to disable the normal effect of an operation. The control can be used to discover how a server might react to a particular update request without updating the directory." +1.3.6.1.4.1.4203.1.11.1 "LDAP Password Modify Extended Operation" "RFC 3062" "An LDAP extended operation to allow modification of user passwords which is not dependent upon the form of the authentication identity nor the password storage mechanism used." +1.3.6.1.4.1.4203.1.11.2 "LDAP Cancel Extended Operation" +1.3.6.1.4.1.4203.1.11.3 "Who Am I? Extended Operation" "draft-zeilenga-ldap-authzid-10.txt" "This specification provides a mechanism for Lightweight Directory Access Protocol (LDAP) clients to obtain the authorization identity which the server has associated with the user or application entity." +1.3.6.1.4.1.4203.666.5.1 "Subentries Control" +1.3.6.1.4.1.4203.666.5.2 "NO OP Control" +1.3.18.0.2.12.1 "The ACL credential controls provide a method to flow a subject's credentials associated with a bind." +1.3.18.0.2.12.5 "tranExtOpInit" +1.3.18.0.2.12.6 "tranExtOpInit" +2.16.840.1.113531.18.2.1 "LDAP_C_SETOPTIONS_OID" +2.16.840.1.113531.18.2.2 "LDAP_C_SETDONTUSECOPY_OID" +2.16.840.1.113531.18.2.3 "LDAP_C_SETLOCALSCOPE_OID" +2.16.840.1.113531.18.2.4 "Return operational attributes as well as user attributes" +2.16.840.1.113531.18.2.5 "Return only subentries" +2.16.840.1.113531.18.2.6 "LDAP_C_SETUSEALIAS_OID" +2.16.840.1.113531.18.2.7 "LDAP_C_SETPREFERCHAIN_OID" +2.16.840.1.113531.18.2.8 "LDAP_C_SETX500DN_OID" +2.16.840.1.113531.18.2.9 "LDAP_C_SETCOPYSHALLDO_OID" +2.16.840.1.113531.18.2.10 "LDAP_C_SETDONTMAPATTRS_OID" +2.16.840.1.113531.18.2.11 "Return normal entries as well as sub-entries" +2.16.840.1.113719.1.27.99.1 "Superior References" +2.16.840.1.113719.1.27.100.1 "ndsToLdapResponse" +2.16.840.1.113719.1.27.100.2 "ndsToLdapRequest" +2.16.840.1.113719.1.27.100.3 "createNamingContextRequest" +2.16.840.1.113719.1.27.100.4 "createNamingContextResponse" +2.16.840.1.113719.1.27.100.5 "mergeNamingContextRequest" +2.16.840.1.113719.1.27.100.6 "mergeNamingContextResponse" +2.16.840.1.113719.1.27.100.7 "addReplicaRequest" +2.16.840.1.113719.1.27.100.8 "addReplicaResponse" +2.16.840.1.113719.1.27.100.9 "refreshLDAPServerRequest" +2.16.840.1.113719.1.27.100.10 "refreshLDAPServerResponse" +2.16.840.1.113719.1.27.100.11 "removeReplicaRequest" +2.16.840.1.113719.1.27.100.12 "removeReplicaResponse" +2.16.840.1.113719.1.27.100.13 "namingContextEntryCountRequest" +2.16.840.1.113719.1.27.100.14 "namingContextEntryCountResponse" +2.16.840.1.113719.1.27.100.15 "changeReplicaTypeRequest" +2.16.840.1.113719.1.27.100.16 "changeReplicaTypeResponse" +2.16.840.1.113719.1.27.100.17 "getReplicaInfoRequest" +2.16.840.1.113719.1.27.100.18 "getReplicaInfoResponse" +2.16.840.1.113719.1.27.100.19 "listReplicaRequest" +2.16.840.1.113719.1.27.100.20 "listReplicaResponse" +2.16.840.1.113719.1.27.100.21 "receiveAllUpdatesRequest" +2.16.840.1.113719.1.27.100.22 "receiveAllUpdatesResponse" +2.16.840.1.113719.1.27.100.23 "sendAllUpdatesRequest" +2.16.840.1.113719.1.27.100.24 "sendAllUpdatesResponse" +2.16.840.1.113719.1.27.100.25 "requestNamingContextSyncRequest" +2.16.840.1.113719.1.27.100.26 "requestNamingContextSyncResponse" +2.16.840.1.113719.1.27.100.27 "requestSchemaSyncRequest" +2.16.840.1.113719.1.27.100.28 "requestSchemaSyncResponse" +2.16.840.1.113719.1.27.100.29 "abortNamingContextOperationRequest" +2.16.840.1.113719.1.27.100.30 "abortNamingContextOperationResponse" +2.16.840.1.113719.1.27.100.31 "Get Bind DN Request" +2.16.840.1.113719.1.27.100.32 "Get Bind DN Response" +2.16.840.1.113719.1.27.100.33 "Get Effective Privileges Request" +2.16.840.1.113719.1.27.100.34 "Get Effective Privileges Response" +2.16.840.1.113719.1.27.100.35 "Set Replication Filter Request" +2.16.840.1.113719.1.27.100.36 "Set Replication Filter Response" +2.16.840.1.113719.1.27.100.37 "Get Replication Filter Request" +2.16.840.1.113719.1.27.100.38 "Get Replication Filter Response" +2.16.840.1.113719.1.27.100.39 "Create Orphan Partition Request" +2.16.840.1.113719.1.27.100.40 "Create Orphan Partition Response" +2.16.840.1.113719.1.27.100.41 "Remove Orphan Partition Request" +2.16.840.1.113719.1.27.100.42 "Remove Orphan Partition Response" +2.16.840.1.113719.1.27.100.43 "Trigger Backlinker Request" +2.16.840.1.113719.1.27.100.44 "Trigger Backlinker Response" +2.16.840.1.113719.1.27.100.47 "Trigger Janitor Request" +2.16.840.1.113719.1.27.100.48 "Trigger Janitor Response" +2.16.840.1.113719.1.27.100.49 "Trigger Limber Request" +2.16.840.1.113719.1.27.100.50 "Trigger Limber Response" +2.16.840.1.113719.1.27.100.51 "Trigger Skulker Request" +2.16.840.1.113719.1.27.100.52 "Trigger Skulker Response" +2.16.840.1.113719.1.27.100.53 "Trigger Schema Synch Request" +2.16.840.1.113719.1.27.100.54 "Trigger Schema Synch Response" +2.16.840.1.113719.1.27.100.55 "Trigger Partition Purge Request" +2.16.840.1.113719.1.27.100.56 "Trigger Partition Purge Response" +2.16.840.1.113719.1.27.100.79 "Monitor Events Request" +2.16.840.1.113719.1.27.100.80 "Monitor Events Response" +2.16.840.1.113719.1.27.100.81 "Event Notification" +2.16.840.1.113719.1.27.101.1 "Duplicate Entry Request" +2.16.840.1.113719.1.27.101.2 "DuplicateSearchResult" +2.16.840.1.113719.1.27.101.3 "DuplicateEntryResponseDone" +2.16.840.1.113719.1.27.101.5 "Simple Password" +2.16.840.1.113719.1.27.101.6 "Forward Reference" +2.16.840.1.113719.1.142.100.1 "startFramedProtocolRequest" +2.16.840.1.113719.1.142.100.2 "startFramedProtocolResponse" +2.16.840.1.113719.1.142.100.3 "ReplicationUpdate" +2.16.840.1.113719.1.142.100.4 "endFramedProtocolRequest" +2.16.840.1.113719.1.142.100.5 "endFramedProtocolResponse" +2.16.840.1.113719.1.142.100.6 "lburpOperationRequest" +2.16.840.1.113719.1.142.100.7 "lburpOperationResponse" +2.16.840.1.113730.3.4 "Netscape LDAPv3 controls" +2.16.840.1.113730.3.4.2 "ManageDsaIT Control" "RFC 3296" "The client may provide the ManageDsaIT control with an operation to indicate that the operation is intended to manage objects within the DSA (server) Information Tree. The control causes Directory-specific entries (DSEs), regardless of type, to be treated as normal entries allowing clients to interrogate and update these entries using LDAP operations." +2.16.840.1.113730.3.4.3 "Persistent Search LDAPv3 control" +2.16.840.1.113730.3.4.4 "Netscape Password Expired LDAPv3 control" +2.16.840.1.113730.3.4.5 "Netscape Password Expiring LDAPv3 control" +2.16.840.1.113730.3.4.6 "Netscape NT Synchronization Client LDAPv3 control" +2.16.840.1.113730.3.4.7 "Entry Change Notification LDAPv3 control" +2.16.840.1.113730.3.4.8 "Transaction ID Request Control" +2.16.840.1.113730.3.4.9 "VLV Request LDAPv3 control" "" "As defined in the 'LDAPv3 Extensions for Virtual List View' IETF document." +2.16.840.1.113730.3.4.10 "VLV Response LDAPv3 control" "" "As defined in the 'LDAPv3 Extensions for Virtual List View' IETF document." +2.16.840.1.113730.3.4.11 "Transaction ID Response Control" +2.16.840.1.113730.3.4.12 "Proxied Authorization (version 1) control" "draft-weltman-ldapv3-proxy-05" "For assuming the identity of another entry for the duration of a request. This has been replaced by a new 'version 2' Proxied Authorization control." +2.16.840.1.113730.3.4.13 "iPlanet Directory Server Replication Update Information Control" +2.16.840.1.113730.3.4.14 "iPlanet Directory Server 'search on specific backend' control" +2.16.840.1.113730.3.4.15 "Authentication Response Control" +2.16.840.1.113730.3.4.16 "Authentication Request Control" +2.16.840.1.113730.3.4.17 "Real Attributes Only Request Control" +2.16.840.1.113730.3.4.18 "LDAP Proxied Authorization Control" "draft-weltman-ldapv3-proxy-06.txt" "The Proxied Authorization Control allows a client to request that an operation be processed under a provided authorization identity [AUTH] instead of as the current authorization identity associated with the connection." +2.16.840.1.113730.3.4.19 "Virtual Attributes Only Request Control" +2.16.840.1.113730.3.4.20 "Use One Backend" +2.16.840.1.113730.3.4.999 "iPlanet Replication Modrdn Extra Mods Control" +2.16.840.1.113730.3.5.3 "iPlanet Start Replication Request Extended Operation" +2.16.840.1.113730.3.5.4 "iPlanet Replication Response Extended Operation" +2.16.840.1.113730.3.5.5 "iPlanet End Replication Request Extended Operation" +2.16.840.1.113730.3.5.6 "iPlanet Replication Entry Request Extended Operation" +2.16.840.1.113730.3.5.7 "iPlanet Bulk Import Start Extended Operation" +2.16.840.1.113730.3.5.8 "iPlanet Bulk Import Finished Extended Operation" +2.16.840.1.113730.3.5.9 "iPlanet Digest authentication calculation" diff --git a/lam/templates/3rdParty/pla/lib/page.php b/lam/templates/3rdParty/pla/lib/page.php new file mode 100644 index 00000000..3d7c1cd9 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/page.php @@ -0,0 +1,510 @@ + + protected $_head; + + # Settings for this application + protected $_app; + + # Default values array. + protected $_default; + + public function __construct($index=null) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # If we done have a configuration, then our IMGDIR and CSS are not defined + if (! defined('IMGDIR')) + define('IMGDIR','images/default'); + if (! defined('CSSDIR')) + define('CSSDIR','css/default'); + + $this->index = $index; + + # To be defined in a configuration file. + $this->_app['title'] = app_name(); + $this->_app['logo'] = IMGDIR.'/logo-small.png'; + + if (! is_null($index)) + $this->_app['urlcss'] = sprintf('%s/%s',CSSDIR,$_SESSION[APPCONFIG]->getValue('appearance','stylesheet')); + else + $this->_app['urlcss'] = sprintf('%s/%s',CSSDIR,'style.css'); + + # Default Values for configurable items. + $this->_default['sysmsg']['error'] = IMGDIR.'/error-big.png'; + $this->_default['sysmsg']['warn'] = IMGDIR.'/warn-big.png'; + $this->_default['sysmsg']['info'] = IMGDIR.'/info-big.png'; + + # Capture any output so far (in case we send some headers below) - there shouldnt be any output anyway. + $preOutput = ''; + + # Try and work around if php compression is on, or the user has set compression in the config. + # type = 1 for user gzip, 0 for php.ini gzip. + $obStatus = ob_get_status(); + if (isset($obStatus['type']) && $obStatus['type'] && $obStatus['status']) { + $preOutput = ob_get_contents(); + ob_end_clean(); + } + + header('Content-type: text/html; charset="UTF-8"'); + if (isCompress()) { + header('Content-Encoding: gzip'); + + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED) + debug_log('Sent COMPRESSED header to browser and discarded (%s)',129,0,__FILE__,__LINE__,__METHOD__,$preOutput); + } + + if (isset($_SESSION[APPCONFIG]) + && $_SESSION[APPCONFIG]->getValue('appearance','compress') + && ini_get('zlib.output_compression')) + $this->setsysmsg(array('title'=>_('Warning'),'body'=>_('WARNING: You cannot have PHP compression and application compression enabled at the same time. Please unset zlib.output_compression or set $config->custom->appearance[\'compress\']=false'),'type'=>'warn')); + + # Turn back on output buffering. + ob_start(); + + # Initial Values + $this->_pageheader[] = ''; + $this->_pageheader[] .= ''."\n"; + } + + /* Add to the HTML Header */ + public function head_add($html) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->_head[] .= $html; + } + + /* Print out the HTML header */ + private function pageheader_print() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # HTML prepage requirements. + foreach ($this->_pageheader as $line) + echo $line."\n"; + + # Page Title + echo ''; + printf(''); + + $DNs = get_request('dn','REQUEST'); + if (is_array($DNs)) + $DNs = ''; + + if (isset($_SESSION[APPCONFIG])) + printf('%s (%s) - %s%s', + $this->_app['title'], + app_version(), + $DNs ? htmlspecialchars($DNs).' ' : '', + $_SESSION[APPCONFIG]->getValue('appearance','page_title')); + else + printf('%s - %s',$this->_app['title'],app_version()); + + echo ''; + # Style sheet. + printf('',$this->_app['urlcss']); + + if (defined('JSDIR')) { + printf('',JSDIR); + echo "\n"; + printf('',JSDIR); + printf('',JSDIR); + echo "\n"; + } + + # HTML head requirements. + if (is_array($this->_head) && count($this->_head)) + foreach ($this->_head as $line) + echo $line."\n"; + + echo ''; + echo "\n"; + } + + private function head_print() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($_SESSION[APPCONFIG])) + $pagetitle = $_SESSION[APPCONFIG]->getValue('appearance','page_title') ? ' - '.$_SESSION[APPCONFIG]->getValue('appearance','page_title') : ''; + else + $pagetitle = ''; + + echo ''; + + echo '
'; + printf('',get_href('sf'),$this->_app['logo']); + + echo ''; + echo '
'; + $empty = true; + if (function_exists('cmd_control_pane')) + foreach (cmd_control_pane('top') as $cmddetails) + if ((isset($cmddetails['enable']) && $cmddetails['enable']) || ! isset($cmddetails['enable'])) { + if (! $empty) + echo ' '; + + printf('%s',$cmddetails['link'],$cmddetails['image']); + + $empty = false; + } + + if ($empty) + echo ' '; + + echo '
'; + echo ''; + echo "\n"; + } + + private function control_print() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + echo ''; + echo ''; + if ($empty) + echo ''; + + echo ''; + echo '
'; + + $empty = true; + if (function_exists('cmd_control_pane')) + foreach (cmd_control_pane('main') as $cmddetails) + if ((isset($cmddetails['enable']) && trim($cmddetails['enable'])) || ! isset($cmddetails['enable'])) { + if (! $empty) + echo ' | '; + + printf('%s',$cmddetails['link'], + (isset($_SESSION[APPCONFIG]) && $_SESSION[APPCONFIG]->getValue('appearance','control_icons')) ? $cmddetails['image'] : $cmddetails['title']); + + $empty = false; + } + + echo ' 
'; + } + + protected function tree() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! isset($_SESSION[APPCONFIG])) + return; + + if (is_null($this->index)) + $this->index = min(array_keys($_SESSION[APPCONFIG]->getServerList())); + + if (count($_SESSION[APPCONFIG]->getServerList()) > 1) { + echo '
'; + echo '
'; + printf('%s:
%s',_('Server Select'), + server_select_list($this->index,false,'index',true,sprintf("onchange=\"tree_unhide('index',%s)\"",$this->index))); + echo '
'; + echo '
'; + echo "\n\n"; + } + + foreach ($_SESSION[APPCONFIG]->getServerList() as $index => $server) { + printf('
',$server->getIndex(),($server->getIndex() == $this->index) ? 'block' : 'none'); + $tree = Tree::getInstance($server->getIndex()); + $tree->draw(); + echo '
'; + echo "\n\n"; + } + } + + public function block_add($side,$object) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_object($object)) + error(sprintf('block_add called with [%s], but it is not an object',serialize($object))); + + $this->_block[$side][] = $object; + } + + private function block_print($side) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! isset($this->_block[$side])) + return; + + printf('',$side); + foreach ($this->_block[$side] as $object) + echo $object->draw($side); + echo ''; + } + + private function sysmsg() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (isset($this->sysmsg)) { + foreach ($this->sysmsg as $index => $details) { + switch ($details['type']) { + case 'error': + $icon = $this->_default['sysmsg']['error']; + break; + + case 'warn': + $icon = $this->_default['sysmsg']['warn']; + break; + + case 'info': + default: + $icon = $this->_default['sysmsg']['info']; + break; + } + + if (isset($details['title'])) + printf('%s%s', + $icon,$details['type'],$details['title']); + + if (isset($details['body'])) + if (is_array($details['body'])) { + echo ''; + foreach ($details['body'] as $line) + printf('%s
',$line); + echo ''; + + } else + printf('%s',$details['body']); + } + } + } + + private function body($raw=false) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Add the Session System Messages + if (isset($_SESSION['sysmsg']) && is_array($_SESSION['sysmsg'])) { + foreach ($_SESSION['sysmsg'] as $msg) + $this->setsysmsg($msg); + + unset($_SESSION['sysmsg']); + } + + if (isset($this->sysmsg)) { + echo ''; + $this->sysmsg(); + echo '
'; + echo "\n"; + } + + if (isset($this->_block['body'])) + foreach ($this->_block['body'] as $object) + echo $object->draw('body',$raw); + } + + private function footer_print() { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + printf('%s
%s
%s', + isCompress() ? '[C]' : ' ', + app_version(), + get_href('logo') ? sprintf('SourceForge.net Logo',get_href('sf'),get_href('logo')) : ' '); + } + + /** + * Only show a particular page frame - used by an AJAX call + */ + public function show($frame,$compress=false,$raw=false) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # If the body is called via AJAX, and compression is enable, we need to compress the output + if ($compress && ob_get_level() && isCompress()) { + ob_end_clean(); + ob_start(); + } + + switch ($frame) { + case 'BODY': + $this->body($raw); + break; + + case 'TREE': + $this->tree(); + break; + + default: + error(sprintf('show called with unknown frame [%s]',$frame),'error','index.php'); + } + + if ($compress && ob_get_level() && isCompress()) { + $output = ob_get_contents(); + ob_end_clean(); + + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED) + debug_log('Sending COMPRESSED output to browser[(%s),%s]',129,0,__FILE__,__LINE__,__METHOD__, + strlen($output),$output); + + print gzencode($output); + } + } + + public function display($filter=array()) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # Control what is displayed. + $display = array( + 'HEAD'=>false, + 'CONTROL'=>false, + 'TREE'=>true, + 'FOOT'=>false + ); + + $display = array_merge($display,$filter); + + # HTML Header + $this->pageheader_print(); + + # Start of body + # Page Header + echo ''; + echo "\n"; + echo ''; + + if ($display['HEAD']) + $this->head_print(); + + # Control Line + if ($display['CONTROL']) { + echo ''; + echo "\n"; + } + + # Left Block + echo ''; + + if ($display['TREE']) { + echo ''; + } + + echo ''; + echo ''; + echo "\n"; + + # Page Footer + if ($display['FOOT']) + $this->footer_print(); + + # Finish HTML + echo '
'; + echo '
'; + $this->control_print(); + echo '
'; + printf('',_('Hide/Unhide the tree'),IMGDIR); + echo '
'; + $this->tree(); + echo '
'; + echo '
'; + echo '
'; + echo "\n"; + $this->body(); + echo '
'; + echo '
'; + echo ''; + echo ''; + + # compress output + if (ob_get_level() && isCompress()) { + $output = ob_get_contents(); + ob_end_clean(); + + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED) + debug_log('Sending COMPRESSED output to browser[(%s),%s]',129,0,__FILE__,__LINE__,__METHOD__, + strlen($output),$output); + + print gzencode($output); + } + } + + public function setsysmsg($data) { + if (defined('DEBUG_ENABLED') && DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',129,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_array($data)) + return; + + if (isset($this->sysmsg)) + $msgnum = count($this->sysmsg) + 1; + else + $msgnum = 1; + + foreach (array('title','body','type') as $index) + if (isset($data[$index])) + $this->sysmsg[$msgnum][$index] = $data[$index]; + } +} + +/** + * This class draws a block. + * + * @package phpLDAPadmin + * @subpackage Page + */ +class block { + private $title; + private $body; + private $foot; + + public function setTitle($html) { + $this->title = $html; + } + + public function setBody($html) { + $this->body = $html; + } + + public function setFooter($html) { + $this->foot = $html; + } + + public function draw($side,$raw=false) { + $output = ''; + + if ($raw) + $output .= $this->body; + + else { + $output .= sprintf('',$side); + + if (isset($this->title)) + $output .= sprintf('',$this->title); + + if (isset($this->body)) + $output .= sprintf('',$this->body); + + if (isset($this->footer)) + $output .= sprintf('',$this->foot); + + $output .= '
%s
%s
%s
'; + } + + return $output; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/query_functions.php b/lam/templates/3rdParty/pla/lib/query_functions.php new file mode 100644 index 00000000..de47630f --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/query_functions.php @@ -0,0 +1,22 @@ + diff --git a/lam/templates/3rdParty/pla/lib/schema_functions.php b/lam/templates/3rdParty/pla/lib/schema_functions.php new file mode 100644 index 00000000..efa0cbc2 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/schema_functions.php @@ -0,0 +1,1539 @@ +oid = $oid; + } + + public function setDescription($desc) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->description = $desc; + } + + public function getOID() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->oid); + + return $this->oid; + } + + public function getDescription() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->description); + + return $this->description; + } + + /** + * Gets whether this objectClass is flagged as obsolete by the LDAP server. + */ + public function getIsObsolete() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_obsolete); + + return $this->is_obsolete; + } + + /** + * Return the objects name. + * + * param boolean $lower Return the name in lower case (default) + * @return string The name + */ + public function getName($lower=true) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name); + + return $lower ? strtolower($this->name) : $this->name; + } +} + +/** + * Represents an LDAP objectClass + * + * @package phpLDAPadmin + * @subpackage Schema + */ +class ObjectClass extends SchemaItem { + # The server ID that this objectclass belongs to. + private $server_id = null; + # Array of objectClass names from which this objectClass inherits + private $sup_classes = array(); + # One of STRUCTURAL, ABSTRACT, or AUXILIARY + private $type; + # Arrays of attribute names that this objectClass requires + private $must_attrs = array(); + # Arrays of attribute names that this objectClass allows, but does not require + private $may_attrs = array(); + # Arrays of attribute names that this objectClass has been forced to MAY attrs, due to configuration + private $force_may = array(); + # Array of objectClasses which inherit from this one (must be set at runtime explicitly by the caller) + private $children_objectclasses = array(); + # The objectclass hierarchy + private $hierarchy = array(); + + /** + * Creates a new ObjectClass object given a raw LDAP objectClass string. + */ + public function __construct($class,$server) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->server_id = $server->getIndex(); + $this->type = $server->getValue('server','schema_oclass_default'); + + $strings = preg_split('/[\s,]+/',$class,-1,PREG_SPLIT_DELIM_CAPTURE); + $str_count = count($strings); + + for ($i=0; $i < $str_count; $i++) { + + switch ($strings[$i]) { + case '(': + break; + + case 'NAME': + if ($strings[$i+1]!='(') { + do { + $i++; + if (strlen($this->name) == 0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + + } while (! preg_match('/\'$/s',$strings[$i])); + + } else { + $i++; + do { + $i++; + if (strlen($this->name) == 0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + + } while (! preg_match('/\'$/s',$strings[$i])); + + do { + $i++; + } while (! preg_match('/\)+\)?/',$strings[$i])); + } + + $this->name = preg_replace('/^\'/','',$this->name); + $this->name = preg_replace('/\'$/','',$this->name); + + if (DEBUG_ENABLED) + debug_log('Case NAME returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->name); + break; + + case 'DESC': + do { + $i++; + if (strlen($this->description) == 0) + $this->description=$this->description.$strings[$i]; + else + $this->description=$this->description.' '.$strings[$i]; + + } while (! preg_match('/\'$/s',$strings[$i])); + + if (DEBUG_ENABLED) + debug_log('Case DESC returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->description); + break; + + case 'OBSOLETE': + $this->is_obsolete = TRUE; + + if (DEBUG_ENABLED) + debug_log('Case OBSOLETE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_obsolete); + break; + + case 'SUP': + if ($strings[$i+1] != '(') { + $i++; + array_push($this->sup_classes,preg_replace("/'/",'',$strings[$i])); + + } else { + $i++; + do { + $i++; + if ($strings[$i] != '$') + array_push($this->sup_classes,preg_replace("/'/",'',$strings[$i])); + + } while (! preg_match('/\)+\)?/',$strings[$i+1])); + } + + if (DEBUG_ENABLED) + debug_log('Case SUP returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->sup_classes); + break; + + case 'ABSTRACT': + $this->type = 'abstract'; + + if (DEBUG_ENABLED) + debug_log('Case ABSTRACT returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->type); + break; + + case 'STRUCTURAL': + $this->type = 'structural'; + + if (DEBUG_ENABLED) + debug_log('Case STRUCTURAL returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->type); + break; + + case 'AUXILIARY': + $this->type = 'auxiliary'; + + if (DEBUG_ENABLED) + debug_log('Case AUXILIARY returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->type); + break; + + case 'MUST': + $attrs = array(); + + $i = $this->parseList(++$i,$strings,$attrs); + + if (DEBUG_ENABLED) + debug_log('parseList returned %d (%s)',8,0,__FILE__,__LINE__,__METHOD__,$i,$attrs); + + foreach ($attrs as $string) { + $attr = new ObjectClass_ObjectClassAttribute($string,$this->name); + + if ($server->isForceMay($attr->getName())) { + array_push($this->force_may,$attr); + array_push($this->may_attrs,$attr); + + } else + array_push($this->must_attrs,$attr); + } + + if (DEBUG_ENABLED) + debug_log('Case MUST returned (%s) (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->must_attrs,$this->force_may); + break; + + case 'MAY': + $attrs = array(); + + $i = $this->parseList(++$i,$strings,$attrs); + + if (DEBUG_ENABLED) + debug_log('parseList returned %d (%s)',8,0,__FILE__,__LINE__,__METHOD__,$i,$attrs); + + foreach ($attrs as $string) { + $attr = new ObjectClass_ObjectClassAttribute($string,$this->name); + array_push($this->may_attrs,$attr); + } + + if (DEBUG_ENABLED) + debug_log('Case MAY returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->may_attrs); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) { + $this->setOID($strings[$i]); + + if (DEBUG_ENABLED) + debug_log('Case default returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->getOID()); + } + break; + } + } + + $this->description = preg_replace("/^\'/",'',$this->description); + $this->description = preg_replace("/\'$/",'',$this->description); + + if (DEBUG_ENABLED) + debug_log('Returning () - NAME (%s), DESCRIPTION (%s), MUST (%s), MAY (%s), FORCE MAY (%s)',9,0,__FILE__,__LINE__,__METHOD__, + $this->name,$this->description,$this->must_attrs,$this->may_attrs,$this->force_may); + } + + /** + * Parse an LDAP schema list + */ + private function parseList($i,$strings,&$attrs) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + /* + * A list starts with a ( followed by a list of attributes separated by $ terminated by ) + * The first token can therefore be a ( or a (NAME or a (NAME) + * The last token can therefore be a ) or NAME) + * The last token may be terminate by more than one bracket + */ + + $string = $strings[$i]; + if (! preg_match('/^\(/',$string)) { + # A bareword only - can be terminated by a ) if the last item + if (preg_match('/\)+$/',$string)) + $string = preg_replace('/\)+$/','',$string); + + array_push($attrs,$string); + + } elseif (preg_match('/^\(.*\)$/',$string)) { + $string = preg_replace('/^\(/','',$string); + $string = preg_replace('/\)+$/','',$string); + array_push($attrs,$string); + + } else { + # Handle the opening cases first + if ($string == '(') { + $i++; + + } elseif (preg_match('/^\(./',$string)) { + $string = preg_replace('/^\(/','',$string); + array_push($attrs,$string); + $i++; + } + + # Token is either a name, a $ or a ')' + # NAME can be terminated by one or more ')' + while (! preg_match('/\)+$/',$strings[$i])) { + $string = $strings[$i]; + if ($string == '$') { + $i++; + continue; + } + + if (preg_match('/\)$/',$string)) + $string = preg_replace('/\)+$/','',$string); + else + $i++; + + array_push($attrs,$string); + } + } + + sort($attrs); + + if (DEBUG_ENABLED) + debug_log('Returning (%d,[%s],[%s])',9,0,__FILE__,__LINE__,__METHOD__,$i,$strings,$attrs); + + return $i; + } + + /** + * This will return all our parent ObjectClass Objects + */ + public function getParents() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ((count($this->sup_classes) == 1) && ($this->sup_classes[0] == 'top')) + return array(); + + $server = $_SESSION[APPCONFIG]->getServer($this->server_id); + $return = array(); + + foreach ($this->sup_classes as $object_class) { + array_push($return,$object_class); + + $oc = $server->getSchemaObjectClass($object_class); + + if ($oc) + $return = array_merge($return,$oc->getParents()); + } + + return $return; + } + + /** + * Gets an array of AttributeType objects that entries of this ObjectClass must define. + * This differs from getMustAttrNames in that it returns an array of AttributeType objects + * + * @param array $parents An array of ObjectClass objects to use when traversing + * the inheritance tree. This presents some what of a bootstrapping problem + * as we must fetch all objectClasses to determine through inheritance which + * attributes this objectClass requires. + * @return array The array of required AttributeType objects. + * + * @see getMustAttrNames + * @see getMayAttrs + * @see getMayAttrNames + */ + public function getMustAttrs($parents=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! $parents) + return $this->must_attrs; + + $server = $_SESSION[APPCONFIG]->getServer($this->server_id); + $attrs = $this->must_attrs; + + foreach ($this->getParents() as $sup_class) { + $sc = $server->getSchemaObjectClass($sup_class); + $attrs = array_merge($attrs,$sc->getMustAttrs()); + } + + masort($attrs,'name,source'); + + # Remove any duplicates + foreach ($attrs as $index => $attr) + if (isset($allattr[$attr->getName()])) + unset($attrs[$index]); + else + $allattr[$attr->getName()] = 1; + + return $attrs; + } + + /** + * Gets an array of AttributeType objects that entries of this ObjectClass may define. + * This differs from getMayAttrNames in that it returns an array of AttributeType objects + * + * @param array $parents An array of ObjectClass objects to use when traversing + * the inheritance tree. This presents some what of a bootstrapping problem + * as we must fetch all objectClasses to determine through inheritance which + * attributes this objectClass provides. + * @return array The array of allowed AttributeType objects. + * + * @see getMustAttrNames + * @see getMustAttrs + * @see getMayAttrNames + * @see AttributeType + */ + public function getMayAttrs($parents=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! $parents) + return $this->may_attrs; + + $server = $_SESSION[APPCONFIG]->getServer($this->server_id); + $attrs = $this->may_attrs; + + foreach ($this->getParents() as $sup_class) { + $sc = $server->getSchemaObjectClass($sup_class); + $attrs = array_merge($attrs,$sc->getMayAttrs()); + } + + masort($attrs,'name,source'); + + # Remove any duplicates + foreach ($attrs as $index => $attr) + if (isset($allattr[$attr->name])) + unset($attrs[$index]); + else + $allattr[$attr->name] = 1; + + return $attrs; + } + + public function getForceMayAttrs() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->force_may; + } + + /** + * Gets an array of attribute names (strings) that entries of this ObjectClass must define. + * This differs from getMustAttrs in that it returns an array of strings rather than + * array of AttributeType objects + * + * @param array $parents An array of ObjectClass objects to use when traversing + * the inheritance tree. This presents some what of a bootstrapping problem + * as we must fetch all objectClasses to determine through inheritance which + * attributes this objectClass provides. + * @return array The array of allowed attribute names (strings). + * + * @see getMustAttrs + * @see getMayAttrs + * @see getMayAttrNames + */ + public function getMustAttrNames($parents=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attr_names = array(); + + foreach ($this->getMustAttrs($parents) as $attr) + array_push($attr_names,$attr->getName()); + + return $attr_names; + } + + /** + * Gets an array of attribute names (strings) that entries of this ObjectClass must define. + * This differs from getMayAttrs in that it returns an array of strings rather than + * array of AttributeType objects + * + * @param array $parents An array of ObjectClass objects to use when traversing + * the inheritance tree. This presents some what of a bootstrapping problem + * as we must fetch all objectClasses to determine through inheritance which + * attributes this objectClass provides. + * @return array The array of allowed attribute names (strings). + * + * @see getMustAttrs + * @see getMayAttrs + * @see getMustAttrNames + */ + public function getMayAttrNames($parents=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $attr_names = array(); + + foreach ($this->getMayAttrs($parents) as $attr) + array_push($attr_names,$attr->getName()); + + return $attr_names; + } + + /** + * Adds an objectClass to the list of objectClasses that inherit + * from this objectClass. + * + * @param String $name The name of the objectClass to add + * @return boolean Returns true on success or false on failure (objectclass already existed for example) + */ + public function addChildObjectClass($name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $name = trim($name); + + foreach ($this->children_objectclasses as $existing_objectclass) + if (strcasecmp($name,$existing_objectclass) == 0) + return false; + + array_push($this->children_objectclasses,$name); + } + + /** + * Returns the array of objectClass names which inherit from this objectClass. + * + * @return Array Names of objectClasses which inherit from this objectClass. + */ + public function getChildObjectClasses() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->children_objectclasses; + } + + /** + * Gets the objectClass names from which this objectClass inherits. + * + * @return array An array of objectClass names (strings) + */ + public function getSupClasses() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + return $this->sup_classes; + } + + /** + * Return if this objectClass is related to $oclass + * + * @param array ObjectClasses that this attribute may be related to + */ + public function isRelated($oclass) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + # If I am in the array, we'll just return false + if (in_array_ignore_case($this->name,$oclass)) + return false; + + $server = $_SESSION[APPCONFIG]->getServer($this->server_id); + + foreach ($oclass as $object_class) { + $oc = $server->getSchemaObjectClass($object_class); + + if ($oc->isStructural() && in_array_ignore_case($this->getName(),$oc->getParents())) + return true; + } + + return false; + } + + /** + * Gets the type of this objectClass: STRUCTURAL, ABSTRACT, or AUXILIARY. + */ + public function getType() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->type); + + return $this->type; + } + + /** + * Adds the specified array of attributes to this objectClass' list of + * MUST attributes. The resulting array of must attributes will contain + * unique members. + * + * @param array $attr An array of attribute names (strings) to add. + */ + private function addMustAttrs($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_array($attr) || ! count($attr)) + return; + + $this->must_attrs = array_values(array_unique(array_merge($this->must_attrs,$attr))); + } + + /** + * Behaves identically to addMustAttrs, but it operates on the MAY + * attributes of this objectClass. + * + * @param array $attr An array of attribute names (strings) to add. + */ + private function addMayAttrs($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_array($attr) || ! count($attr)) + return; + + $this->may_attrs = array_values(array_unique(array_merge($this->may_attrs,$attr))); + } + + /** + * Determine if an array is listed in the force_may attrs + */ + public function isForceMay($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->force_may as $forcemay) + if ($forcemay->getName() == $attr) + return true; + + return false; + } + + public function isStructural() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if ($this->type == 'structural') + return true; + else + return false; + } +} + +/** + * A simple class for representing AttributeTypes used only by the ObjectClass class. + * Users should never instantiate this class. It represents an attribute internal to + * an ObjectClass. If PHP supported inner-classes and variable permissions, this would + * be interior to class ObjectClass and flagged private. The reason this class is used + * and not the "real" class AttributeType is because this class supports the notion of + * a "source" objectClass, meaning that it keeps track of which objectClass originally + * specified it. This class is therefore used by the class ObjectClass to determine + * inheritance. + * + * @package phpLDAPadmin + * @subpackage Schema + */ +class ObjectClass_ObjectClassAttribute { + # This Attribute's name (needs to be public, as we sort on it with masort). + public $name; + # This Attribute's root (needs to be public, as we sort on it with masort). + public $source; + + /** + * Creates a new ObjectClass_ObjectClassAttribute with specified name and source objectClass. + * + * @param string $name the name of the new attribute. + * @param string $source the name of the ObjectClass which specifies this attribute. + */ + public function __construct($name,$source) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->name = $name; + $this->source = $source; + } + + # Gets this attribute's name + public function getName($lower=true) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name); + + return $lower ? strtolower($this->name) : $this->name; + } + + # Gets the name of the ObjectClass which originally specified this attribute. + public function getSource() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->source); + + return $this->source; + } +} + +/** + * Represents an LDAP AttributeType + * + * @package phpLDAPadmin + * @subpackage Schema + */ +class AttributeType extends SchemaItem { + # The attribute from which this attribute inherits (if any) + private $sup_attribute = null; + # The equality rule used + private $equality = null; + # The ordering of the attributeType + private $ordering = null; + # Boolean: supports substring matching? + private $sub_str = null; + # The full syntax string, ie 1.2.3.4{16} + private $syntax = null; + private $syntax_oid = null; + # boolean: is single valued only? + private $is_single_value = false; + # boolean: is collective? + private $is_collective = false; + # boolean: can use modify? + private $is_no_user_modification = false; + # The usage string set by the LDAP schema + private $usage = null; + # An array of alias attribute names, strings + private $aliases = array(); + # The max number of characters this attribute can be + private $max_length = null; + # A string description of the syntax type (taken from the LDAPSyntaxes) + private $type = null; + # An array of objectClasses which use this attributeType (must be set by caller) + private $used_in_object_classes = array(); + # A list of object class names that require this attribute type. + private $required_by_object_classes = array(); + # This attribute has been forced a MAY attribute by the configuration. + private $forced_as_may = false; + + /** + * Creates a new AttributeType object from a raw LDAP AttributeType string. + */ + public function __construct($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $strings = preg_split('/[\s,]+/',$attr,-1,PREG_SPLIT_DELIM_CAPTURE); + + for($i=0; $iname)==0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + # This attribute has no aliases + $this->aliases = array(); + + } else { + $i++; + do { + # In case we came here becaues of a (' + if (preg_match('/^\(/',$strings[$i])) + $strings[$i] = preg_replace('/^\(/','',$strings[$i]); + else + $i++; + + if (strlen($this->name) == 0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + # Add alias names for this attribute + while ($strings[++$i] != ')') { + $alias = $strings[$i]; + $alias = preg_replace("/^\'/",'',$alias); + $alias = preg_replace("/\'$/",'',$alias); + $this->addAlias($alias); + } + } + + if (DEBUG_ENABLED) + debug_log('Case NAME returned (%s) (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->name,$this->aliases); + break; + + case 'DESC': + do { + $i++; + if (strlen($this->description)==0) + $this->description=$this->description.$strings[$i]; + else + $this->description=$this->description.' '.$strings[$i]; + } while (! preg_match("/\'$/s",$strings[$i])); + + if (DEBUG_ENABLED) + debug_log('Case DESC returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->description); + break; + + case 'OBSOLETE': + $this->is_obsolete = TRUE; + + if (DEBUG_ENABLED) + debug_log('Case OBSOLETE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_obsolete); + break; + + case 'SUP': + $i++; + $this->sup_attribute = $strings[$i]; + + if (DEBUG_ENABLED) + debug_log('Case SUP returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->sup_attribute); + break; + + case 'EQUALITY': + $i++; + $this->equality = $strings[$i]; + + if (DEBUG_ENABLED) + debug_log('Case EQUALITY returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->equality); + break; + + case 'ORDERING': + $i++; + $this->ordering = $strings[$i]; + + if (DEBUG_ENABLED) + debug_log('Case ORDERING returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->ordering); + break; + + case 'SUBSTR': + $i++; + $this->sub_str = $strings[$i]; + + if (DEBUG_ENABLED) + debug_log('Case SUBSTR returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->sub_str); + break; + + case 'SYNTAX': + $i++; + $this->syntax = $strings[$i]; + $this->syntax_oid = preg_replace('/{\d+}$/','',$this->syntax); + + # Does this SYNTAX string specify a max length (ie, 1.2.3.4{16}) + if (preg_match('/{(\d+)}$/',$this->syntax,$this->max_length)) + $this->max_length = $this->max_length[1]; + else + $this->max_length = null; + + if ($i < count($strings) - 1 && $strings[$i+1] == '{') { + do { + $i++; + $this->name .= ' '.$strings[$i]; + } while ($strings[$i] != '}'); + } + + if (DEBUG_ENABLED) + debug_log('Case SYNTAX returned (%s) (%s) (%s)',8,0,__FILE__,__LINE__,__METHOD__, + $this->syntax,$this->syntax_oid,$this->max_length); + break; + + case 'SINGLE-VALUE': + $this->is_single_value = TRUE; + if (DEBUG_ENABLED) + debug_log('Case SINGLE-VALUE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_single_value); + break; + + case 'COLLECTIVE': + $this->is_collective = TRUE; + + if (DEBUG_ENABLED) + debug_log('Case COLLECTIVE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_collective); + break; + + case 'NO-USER-MODIFICATION': + $this->is_no_user_modification = TRUE; + + if (DEBUG_ENABLED) + debug_log('Case NO-USER-MODIFICATION returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_no_user_modification); + break; + + case 'USAGE': + $i++; + $this->usage = $strings[$i]; + + if (DEBUG_ENABLED) + debug_log('Case USAGE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->usage); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) { + $this->setOID($strings[$i]); + + if (DEBUG_ENABLED) + debug_log('Case default returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->getOID()); + } + } + } + + $this->name = preg_replace("/^\'/",'',$this->name); + $this->name = preg_replace("/\'$/",'',$this->name); + $this->description = preg_replace("/^\'/",'',$this->description); + $this->description = preg_replace("/\'$/",'',$this->description); + $this->syntax = preg_replace("/^\'/",'',$this->syntax); + $this->syntax = preg_replace("/\'$/",'',$this->syntax); + $this->syntax_oid = preg_replace("/^\'/",'',$this->syntax_oid); + $this->syntax_oid = preg_replace("/\'$/",'',$this->syntax_oid); + $this->sup_attribute = preg_replace("/^\'/",'',$this->sup_attribute); + $this->sup_attribute = preg_replace("/\'$/",'',$this->sup_attribute); + + if (DEBUG_ENABLED) + debug_log('Returning ()',9,0,__FILE__,__LINE__,__METHOD__); + } + + /** + * Gets this attribute's usage string as defined by the LDAP server + * + * @return string + */ + public function getUsage() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->usage); + + return $this->usage; + } + + /** + * Gets this attribute's parent attribute (if any). If this attribute does not + * inherit from another attribute, null is returned. + * + * @return string + */ + public function getSupAttribute() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->sup_attribute); + + return $this->sup_attribute; + } + + /** + * Gets this attribute's equality string + * + * @return string + */ + public function getEquality() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->equality); + + return $this->equality; + } + + /** + * Gets this attribute's ordering specification. + * + * @return string + */ + public function getOrdering() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->ordering); + + return $this->ordering; + } + + /** + * Gets this attribute's substring matching specification + * + * @return string + */ + public function getSubstr() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->sub_str); + + return $this->sub_str; + } + + /** + * Gets the names of attributes that are an alias for this attribute (if any). + * + * @return array An array of names of attributes which alias this attribute or + * an empty array if no attribute aliases this object. + */ + public function getAliases() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->aliases); + + return $this->aliases; + } + + /** + * Returns whether the specified attribute is an alias for this one (based on this attribute's alias list). + * + * @param string $attr_name The name of the attribute to check. + * @return boolean True if the specified attribute is an alias for this one, or false otherwise. + */ + public function isAliasFor($attr_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->aliases as $alias_attr_name) + if (strcasecmp($alias_attr_name,$attr_name) == 0) + return true; + + return false; + } + + /** + * Gets this attribute's raw syntax string (ie: "1.2.3.4{16}"). + * + * @return string The raw syntax string + */ + public function getSyntaxString() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->syntax); + + return $this->syntax; + } + + /** + * Gets this attribute's syntax OID. Differs from getSyntaxString() in that this + * function only returns the actual OID with any length specification removed. + * Ie, if the syntax string is "1.2.3.4{16}", this function only retruns + * "1.2.3.4". + * + * @return string The syntax OID string. + */ + public function getSyntaxOID() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->syntax_oid); + + return $this->syntax_oid; + } + + /** + * Gets this attribute's the maximum length. If no maximum is defined by the LDAP server, null is returned. + * + * @return int The maximum length (in characters) of this attribute or null if no maximum is specified. + */ + public function getMaxLength() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->max_length); + + return $this->max_length; + } + + /** + * Gets whether this attribute is single-valued. If this attribute only supports single values, true + * is returned. If this attribute supports multiple values, false is returned. + * + * @return boolean Returns true if this attribute is single-valued or false otherwise. + */ + public function getIsSingleValue() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_single_value); + + return $this->is_single_value; + } + + /** + * Sets whether this attribute is single-valued. + * + * @param boolean $is + */ + public function setIsSingleValue($is) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->is_single_value = $is; + } + + /** + * Gets whether this attribute is collective. + * + * @return boolean Returns true if this attribute is collective and false otherwise. + */ + public function getIsCollective() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_collective); + + return $this->is_collective; + } + + /** + * Gets whether this attribute is not modifiable by users. + * + * @return boolean Returns true if this attribute is not modifiable by users. + */ + public function getIsNoUserModification() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_no_user_modification); + + return $this->is_no_user_modification; + } + + /** + * Gets this attribute's type + * + * @return string The attribute's type. + */ + public function getType() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->type); + + return $this->type; + } + + /** + * Removes an attribute name from this attribute's alias array. + * + * @param string $remove_alias_name The name of the attribute to remove. + * @return boolean true on success or false on failure (ie, if the specified + * attribute name is not found in this attribute's list of aliases) + */ + public function removeAlias($remove_alias_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->aliases as $i => $alias_name) { + + if (strcasecmp($alias_name,$remove_alias_name) == 0) { + unset($this->aliases[$i]); + + $this->aliases = array_values($this->aliases); + return true; + } + } + return false; + } + + /** + * Adds an attribute name to the alias array. + * + * @param string $alias The name of a new attribute to add to this attribute's list of aliases. + */ + public function addAlias($alias) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + array_push($this->aliases,$alias); + } + + /** + * Sets this attriute's name. + * + * @param string $name The new name to give this attribute. + */ + public function setName($name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->name = $name; + } + + /** + * Sets this attriute's SUP attribute (ie, the attribute from which this attribute inherits). + * + * @param string $attr The name of the new parent (SUP) attribute + */ + public function setSupAttribute($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->sup_attribute = $attr; + } + + /** + * Sets this attribute's list of aliases. + * + * @param array $aliases The array of alias names (strings) + */ + public function setAliases($aliases) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->aliases = $aliases; + } + + /** + * Sets this attribute's type. + * + * @param string $type The new type. + */ + public function setType($type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->type = $type; + } + + /** + * Adds an objectClass name to this attribute's list of "used in" objectClasses, + * that is the list of objectClasses which provide this attribute. + * + * @param string $name The name of the objectClass to add. + */ + public function addUsedInObjectClass($name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->used_in_object_classes as $used_in_object_class) { + if (DEBUG_ENABLED) + debug_log('Checking (%s) with (%s)',8,0,__FILE__,__LINE__,__METHOD__,$used_in_object_class,$name); + + if (strcasecmp($used_in_object_class,$name) == 0) + return false; + } + + array_push($this->used_in_object_classes,$name); + } + + /** + * Gets the list of "used in" objectClasses, that is the list of objectClasses + * which provide this attribute. + * + * @return array An array of names of objectclasses (strings) which provide this attribute + */ + public function getUsedInObjectClasses() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->used_in_object_classes); + + return $this->used_in_object_classes; + } + + /** + * Adds an objectClass name to this attribute's list of "required by" objectClasses, + * that is the list of objectClasses which must have this attribute. + * + * @param string $name The name of the objectClass to add. + */ + public function addRequiredByObjectClass($name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->required_by_object_classes as $required_by_object_class) + if (strcasecmp($required_by_object_class,$name) == 0) + return false; + + array_push($this->required_by_object_classes,$name); + } + + /** + * Gets the list of "required by" objectClasses, that is the list of objectClasses + * which provide must have attribute. + * + * @return array An array of names of objectclasses (strings) which provide this attribute + */ + public function getRequiredByObjectClasses() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->required_by_object_classes); + + return $this->required_by_object_classes; + } + + /** + * This function will mark this attribute as a forced MAY attribute + */ + public function setForceMay() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->forced_as_may = true; + } + + public function isForceMay() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->forced_as_may); + + return $this->forced_as_may; + } +} + +/** + * Represents an LDAP Syntax + * + * @package phpLDAPadmin + * @subpackage Schema + */ +class Syntax extends SchemaItem { + /** + * Creates a new Syntax object from a raw LDAP syntax string. + */ + public function __construct($class) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $strings = preg_split('/[\s,]+/',$class,-1,PREG_SPLIT_DELIM_CAPTURE); + + for($i=0; $idescription) == 0) + $this->description=$this->description.$strings[$i]; + else + $this->description=$this->description.' '.$strings[$i]; + } while (! preg_match("/\'$/s",$strings[$i])); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) + $this->setOID($strings[$i]); + } + } + + $this->description = preg_replace("/^\'/",'',$this->description); + $this->description = preg_replace("/\'$/",'',$this->description); + } +} + +/** + * Represents an LDAP MatchingRule + * + * @package phpLDAPadmin + * @subpackage Schema + */ +class MatchingRule extends SchemaItem { + # This rule's syntax OID + private $syntax = null; + # An array of attribute names who use this MatchingRule + private $used_by_attrs = array(); + + /** + * Creates a new MatchingRule object from a raw LDAP MatchingRule string. + */ + function __construct($strings) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $strings = preg_split('/[\s,]+/',$strings,-1,PREG_SPLIT_DELIM_CAPTURE); + + for ($i=0; $iname) == 0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + } while (! preg_match("/\'$/s",$strings[$i])); + + } else { + $i++; + do { + $i++; + if (strlen($this->name) == 0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + } while (! preg_match("/\'$/s",$strings[$i])); + + do { + $i++; + } while (! preg_match('/\)+\)?/',$strings[$i])); + } + + $this->name = preg_replace("/^\'/",'',$this->name); + $this->name = preg_replace("/\'$/",'',$this->name); + break; + + case 'DESC': + do { + $i++; + if (strlen($this->description)==0) + $this->description=$this->description.$strings[$i]; + else + $this->description=$this->description.' '.$strings[$i]; + } while (! preg_match("/\'$/s",$strings[$i])); + break; + + case 'OBSOLETE': + $this->is_obsolete = TRUE; + break; + + case 'SYNTAX': + $this->syntax = $strings[++$i]; + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) + $this->setOID($strings[$i]); + } + } + $this->description = preg_replace("/^\'/",'',$this->description); + $this->description = preg_replace("/\'$/",'',$this->description); + } + + /** + * Sets the list of used_by_attrs to the array specified by $attrs; + * + * @param array $attrs The array of attribute names (strings) which use this MatchingRule + */ + public function setUsedByAttrs($attrs) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->used_by_attrs = $attrs; + } + + /** + * Adds an attribute name to the list of attributes who use this MatchingRule + * + * @return true if the attribute was added and false otherwise (already in the list) + */ + public function addUsedByAttr($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->used_by_attrs as $attr_name) + if (strcasecmp($attr_name,$attr) == 0) + return false; + + array_push($this->used_by_attrs,$attr); + + return true; + } + + /** + * Gets an array of attribute names (strings) which use this MatchingRule + * + * @return array The array of attribute names (strings). + */ + public function getUsedByAttrs() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->used_by_attrs); + + return $this->used_by_attrs; + } +} + +/** + * Represents an LDAP schema matchingRuleUse entry + * + * @package phpLDAPadmin + * @subpackage Schema + */ +class MatchingRuleUse extends SchemaItem { + # An array of attribute names who use this MatchingRule + private $used_by_attrs = array(); + + function __construct($strings) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $strings = preg_split('/[\s,]+/',$strings,-1,PREG_SPLIT_DELIM_CAPTURE); + + for($i=0; $iname) || strlen($this->name) == 0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + } else { + $i++; + do { + $i++; + if (strlen($this->name) == 0) + $this->name = $strings[$i]; + else + $this->name .= ' '.$strings[$i]; + } while (! preg_match("/\'$/s",$strings[$i])); + + do { + $i++; + } while (! preg_match('/\)+\)?/',$strings[$i])); + } + + $this->name = preg_replace("/^\'/",'',$this->name); + $this->name = preg_replace("/\'$/",'',$this->name); + break; + + case 'APPLIES': + if ($strings[$i+1] != '(') { + # Has a single attribute name + $i++; + $this->used_by_attrs = array($strings[$i]); + + } else { + # Has multiple attribute names + $i++; + while ($strings[$i] != ')') { + $i++; + $new_attr = $strings[$i]; + $new_attr = preg_replace("/^\'/",'',$new_attr); + $new_attr = preg_replace("/\'$/",'',$new_attr); + array_push($this->used_by_attrs,$new_attr); + $i++; + } + } + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) + $this->setOID($strings[$i]); + } + } + + sort($this->used_by_attrs); + } + + /** + * Gets an array of attribute names (strings) which use this MatchingRuleUse object. + * + * @return array The array of attribute names (strings). + */ + public function getUsedByAttrs() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->used_by_attrs); + + return $this->used_by_attrs; + } +} +?> diff --git a/lam/templates/3rdParty/pla/lib/session_functions.php b/lam/templates/3rdParty/pla/lib/session_functions.php new file mode 100644 index 00000000..809d8893 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/session_functions.php @@ -0,0 +1,31 @@ + diff --git a/lam/templates/3rdParty/pla/lib/syslog.php b/lam/templates/3rdParty/pla/lib/syslog.php new file mode 100644 index 00000000..25ca16c1 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/syslog.php @@ -0,0 +1,88 @@ + and AlcĂ´ve + * @package phpLDAPadmin + */ + +# If config_default.php hasnt been called yet, then return. +if (! defined('APPCONFIG') || ! isset($_SESSION[APPCONFIG])) + return; + +# Initialize syslog +if ($_SESSION[APPCONFIG]->getValue('debug','syslog') && function_exists('syslog')) { + openlog('phpldapadmin',LOG_ODELAY,LOG_DAEMON); +} + +/** + * Verify that syslog logging is activated in the config via the + * debug->syslog variable and does a call to the syslog() function is it + * is true. + * + * @param emergency Syslog emergency. + * @param log_string String to log. + */ +function syslog_msg($emergency,$log_string) { + if (! function_exists('syslog') || ! isset($_SESSION[APPCONFIG]) || ! $_SESSION[APPCONFIG]->getValue('debug','syslog')) + return; + + return syslog($emergency,$log_string); +} + +/** + * Issue an error message via syslog. + * + * @param log_string Log message to send to syslog. + * @return true on success. + */ +function syslog_err($log_string) { + return syslog_msg(LOG_ERR,$log_string); +} + +/** + * Issue a warning message via syslog. + * + * @param log_string Log message to send to syslog. + * @return true on success. + */ +function syslog_warning($log_string) { + return syslog_msg(LOG_WARNING,$log_string); +} + +/** + * Issue a notice message via syslog. + * + * @param log_string Log message to send to syslog. + * @return true on success. + */ +function syslog_notice($log_string) { + return syslog_msg(LOG_NOTICE,$log_string); +} + +/** + * Issue a debug message via syslog, only if $log_level is set to + * 'debug' from the config file. + * + * @param log_string Log message to send to syslog. + * @return true on success or if debug log is not activated. + */ +function syslog_debug($log_string) { + return syslog_msg(LOG_DEBUG,$log_string); +} +?> diff --git a/lam/templates/3rdParty/pla/lib/template_functions.php b/lam/templates/3rdParty/pla/lib/template_functions.php new file mode 100644 index 00000000..bb3b6069 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/template_functions.php @@ -0,0 +1,23 @@ + diff --git a/lam/templates/3rdParty/pla/lib/xml2array.php b/lam/templates/3rdParty/pla/lib/xml2array.php new file mode 100644 index 00000000..d260c9e4 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/xml2array.php @@ -0,0 +1,144 @@ +stack[count($this->stack)] = &$pos; + $this->stack_ref = &$pos; + } + + private function pop_pos() { + unset($this->stack[count($this->stack) - 1]); + $this->stack_ref = &$this->stack[count($this->stack) - 1]; + } + + public function parseXML($strInputXML,$filename) { + $this->resParser = xml_parser_create(); + xml_set_object($this->resParser,$this); + xml_set_element_handler($this->resParser,'tagOpen','tagClosed'); + + xml_set_character_data_handler($this->resParser,'tagData'); + + $this->push_pos($this->arrOutput); + + $this->strXmlData = xml_parse($this->resParser,$strInputXML); + + if (! $this->strXmlData) + die(sprintf('XML error: %s at line %d in file %s', + xml_error_string(xml_get_error_code($this->resParser)), + xml_get_current_line_number($this->resParser), + $filename)); + + xml_parser_free($this->resParser); + + $output = array(); + foreach ($this->arrOutput as $key => $values) + $output[$key] = $this->cleanXML($values); + + #return $this->arrOutput; + return $output; + } + + private function tagOpen($parser,$name,$attrs) { + $name = strtolower($name); + + if (isset($this->stack_ref[$name])) { + if (! isset($this->stack_ref[$name][0])) { + $tmp = $this->stack_ref[$name]; + unset($this->stack_ref[$name]); + $this->stack_ref[$name][0] = $tmp; + } + + $cnt = count($this->stack_ref[$name]); + $this->stack_ref[$name][$cnt] = array(); + if (isset($attrs)) + $this->stack_ref[$name][$cnt] = $attrs; + + $this->push_pos($this->stack_ref[$name][$cnt]); + + } else { + $this->stack_ref[$name]=array(); + + if (isset($attrs)) + $this->stack_ref[$name]=$attrs; + + $this->push_pos($this->stack_ref[$name]); + } + } + + private function tagData($parser,$tagData) { + if (trim($tagData) != '') { + + if (isset($this->stack_ref['#text'])) + $this->stack_ref['#text'] .= $tagData; + else + $this->stack_ref['#text'] = $tagData; + } + } + + private function tagClosed($parser,$name) { + $this->pop_pos(); + } + + /** + * This function will parse an XML array and make a normal array. + * + * @return array - Clean XML data + */ + private function cleanXML($details) { + # Quick processing for the final branch of the XML array. + if (is_array($details) && isset($details['#text'])) + return $details['#text']; + + elseif (is_array($details) && isset($details['ID']) && count($details) == 1) + return $details['ID']; + + $cleanXML = array(); + + # Quick processing for the final branch, when it holds the ID and values. + if (is_array($details) && isset($details['ID']) && count($details) > 1) { + $key = $details['ID']; + unset($details['ID']); + $cleanXML[$key] = $this->cleanXML($details); + $details = array(); + } + + # More detailed processing... + if (is_array($details)) + foreach ($details as $key => $values) + if (is_numeric($key) && isset($values['ID']) && count($values) > 1) { + $key = $values['ID']; + unset($values['ID']); + $cleanXML[$key] = $this->cleanXML($values); + + } elseif (isset($values['#text'])) + $cleanXML[$key] = $this->cleanXML($values); + + elseif (is_array($values)) + $cleanXML[$key] = $this->cleanXML($values); + + if (! $cleanXML) + return $details; + else + return $cleanXML; + } +} diff --git a/lam/templates/3rdParty/pla/lib/xmlTemplates.php b/lam/templates/3rdParty/pla/lib/xmlTemplates.php new file mode 100644 index 00000000..08389de5 --- /dev/null +++ b/lam/templates/3rdParty/pla/lib/xmlTemplates.php @@ -0,0 +1,495 @@ +server_id = $server_id; + $server = $_SESSION[APPCONFIG]->getServer($this->server_id); + $custom_prefix = $server->getValue('custom','pages_prefix'); + $class = $this->getClassVars(); + $changed = false; + + # Try to get the templates from our CACHE. + if ($this->templates = get_cached_item($server_id,$class['item'])) { + if (DEBUG_ENABLED) + debug_log('Using CACHED templates',4,0,__FILE__,__LINE__,__METHOD__); + + # See if the template_time has expired to see if we should reload the templates. + foreach ($this->templates as $index => $template) { + # If the file no longer exists, we'll delete the template. + if (! file_exists($template->getFileName())) { + unset($this->templates[$index]); + $changed = true; + + system_message(array( + 'title'=>_('Template XML file removed.'), + 'body'=>sprintf('%s %s (%s)',_('Template XML file has removed'),$template->getName(false),$template->getType()), + 'type'=>'info','special'=>true)); + + continue; + } + + if (($template->getReadTime() < (time()-$class['cachetime'])) + && (filectime($template->getFileName()) > $template->getReadTime())) { + + system_message(array( + 'title'=>_('Template XML file changed.'), + 'body'=>sprintf('%s %s (%s)',_('Template XML file has changed and been reread'),$template->getName(false),$template->getType()), + 'type'=>'info','special'=>true)); + + $changed = true; + eval(sprintf('$this->templates[$index] = new %s($this->server_id,$template->getName(false),$template->getFileName(),$template->getType(),$index);',$class['name'])); + } + } + + if (DEBUG_ENABLED) + debug_log('Templates refreshed',4,0,__FILE__,__LINE__,__METHOD__); + + # See if there are any new template files + $index = max(array_keys($this->templates))+1; + foreach ($class['types'] as $type) { + $dir = $class['dir'].$type; + $dh = opendir($dir); + if (! $type) + $type = 'template'; + + while ($file = readdir($dh)) { + # Ignore any files that are not XML files. + if (! preg_match('/.xml$/',$file)) + continue; + + # Ignore any files that are not the predefined custom files. + if ($_SESSION[APPCONFIG]->getValue('appearance','custom_templates_only') + && ! preg_match("/^${custom_prefix}/",$file)) + continue; + + $filename = sprintf('%s/%s',$dir,$file); + + if (! in_array($filename,$this->getTemplateFiles())) { + $templatename = preg_replace('/.xml$/','',$file); + + eval(sprintf('$this->templates[$index] = new %s($this->server_id,$templatename,$filename,$type,$index);',$class['name'])); + $index++; + + $changed = true; + + system_message(array( + 'title'=>_('New Template XML found.'), + 'body'=>sprintf('%s %s (%s)',_('A new template XML file has been loaded'),$file,$type), + 'type'=>'info','special'=>true)); + } + } + } + + } else { + if (DEBUG_ENABLED) + debug_log('Parsing templates',4,0,__FILE__,__LINE__,__METHOD__); + + # Need to reset this, as get_cached_item() returns null if nothing cached. + $this->templates = array(); + $changed = true; + + $counter = 0; + foreach ($class['types'] as $type) { + $dir = $class['dir'].$type; + $dh = opendir($class['dir'].$type); + if (! $type) + $type = 'template'; + + while ($file = readdir($dh)) { + # Ignore any files that are not XML files. + if (! preg_match('/.xml$/',$file)) + continue; + + # Ignore any files that are not the predefined custom files. + if ($_SESSION[APPCONFIG]->getValue('appearance','custom_templates_only') + && ! preg_match("/^${custom_prefix}/",$file)) + continue; + + $filename = sprintf('%s/%s',$dir,$file); + + # Store the template + $templatename = preg_replace('/.xml$/','',$file); + eval(sprintf('$this->templates[$counter] = new %s($this->server_id,$templatename,$filename,$type,$counter);',$class['name'])); + $counter++; + } + } + } + + if (DEBUG_ENABLED) + debug_log('Templates loaded',4,0,__FILE__,__LINE__,__METHOD__); + + if ($changed) { + masort($this->templates,'title'); + set_cached_item($server_id,$class['item'],'null',$this->templates); + } + } + + /** + * This will return our custom class variables, used by the parent to create objects. + */ + private function getClassVars() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $class = array(); + + switch (get_class($this)) { + case 'Queries': + $class['item'] = 'query'; + $class['name'] = 'Query'; + $class['cachetime'] = $_SESSION[APPCONFIG]->getValue('cache','query_time'); + $class['types'] = array(''); + $class['dir'] = QUERYDIR; + + break; + + case 'Templates': + $class['item'] = 'template'; + $class['name'] = 'Template'; + $class['cachetime'] = $_SESSION[APPCONFIG]->getValue('cache','template_time'); + $class['types'] = array('creation','modification'); + $class['dir'] = TMPLDIR; + + break; + + default: + debug_dump_backtrace(sprintf('Unknown class %s',get_class($this)),1); + } + + return $class; + } + + /** + * Return a list of templates by their type + * This function should return a sorted list, as the array is built sorted. + * + * @param string Type of template, eg: creation, modification + * @param boolean Exclude templates purposely disabled. + * @return array List of templates of the type + */ + public function getTemplates($type=null,$container=null,$disabled=false) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + if (is_array($this->templates)) + foreach ($this->templates as $details) { + + # Clone this, as we'll disable some templates, as a result of the container being requested. + $template = clone $details; + if (! is_null($container) && ($regexp = $template->getRegExp()) && (! @preg_match('/'.$regexp.'/i',$container))) { + $template->setInvalid(_('This template is not valid in this container'),true); + + if ($_SESSION[APPCONFIG]->getValue('appearance','hide_template_regexp')) + $template->setInvisible(); + } + + if ($template->isVisible() && (! $disabled || ! $template->isAdminDisabled())) + if (is_null($type) || (! is_null($type) && $template->isType($type))) + array_push($result,$template); + } + + return $result; + } + + /** + * Return a template by its ID + * + * @param string The template ID as it was when it was generated (normally used in $_REQUEST vars). + * @return object Template (or default template if the ID doesnt exist) + */ + function getTemplate($templateid) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $class = $this->getClassVars(); + + foreach ($this->templates as $template) + if ($template->getID() === $templateid) + return clone $template; + + # If we get here, the template ID didnt exist, so return a blank template, which be interpreted as the default template + eval(sprintf('$object = new %s($this->server_id,null,null,"default");',$class['name'])); + return $object; + } + + /** + * Get a list of template filenames. + */ + private function getTemplateFiles() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + foreach ($this->templates as $template) + array_push($result,$template->getFileName()); + + return $result; + } +} + +/** + * XML Template Class + * + * @package phpLDAPadmin + * @subpackage Templates + */ +abstract class xmlTemplate { + # Server ID that the template is linked to + protected $server_id; + # Template unique ID + protected $id; + # Template name - as extracted from the filename + protected $name; + # Template type - creation/modification + protected $type; + # Time this object was created + protected $readtime; + # Template file name + protected $filename; + # The TEMPLATE attributes as per the template definition, or the DN entry + protected $attributes = array(); + + public function __construct($server_id,$name=null,$filename=null,$type=null,$id=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->server_id = $server_id; + $this->name = $name; + $this->type = $type; + $this->filename = $filename; + $this->readtime = time(); + $this->id = $id; + + # If there is no filename, then this template is a default template. + if (is_null($filename)) + return; + + # If we have a filename, parse the template file and build the object. + $objXML = new xml2array(); + $xmldata = $objXML->parseXML(file_get_contents($filename),$filename); + $this->storeTemplate($xmldata); + } + + /** + * Get an attribute ID + * + * @param string The Attribute being searched. + * @return int Attribute ID in the array + */ + protected function getAttrID($attr) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->attributes as $index => $attribute) + if (strtolower($attr) == $attribute->getName() || in_array(strtolower($attr),$attribute->getAliases())) + return $index; + + return null; + } + + /** + * Get the Template filename. + */ + public function getFileName() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->filename); + + return $this->filename; + } + + /** + * Return the template by ID + */ + public function getID() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->id); + + if ($this->name) + return sprintf('%s:%s',$this->getName(false),$this->id); + else + return 'none'; + } + + /** + * Return the template name + * + * @param boolean Force the name to be lowercase (default) + */ + public function getName($lower=true) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name); + + if ($lower) + return strtolower($this->name); + else + return $this->name; + } + + /** + * Get the Template read time. + */ + public function getReadTime() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->readtime); + + return $this->readtime; + } + + /** + * Return this LDAP Server object + * + * @return object DataStore Server + */ + protected function getServer() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs); + + return $_SESSION[APPCONFIG]->getServer($this->getServerID()); + } + + /** + * Return the LDAP server ID + * + * @return int Server ID + */ + protected function getServerID() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->server_id); + + return $this->server_id; + } + + /** + * Test if a template is of a type + * + * @return boolean + */ + public function isType($type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->type); + + if ($this->type == $type) + return true; + else + return false; + } + + /** + * Return the template type + */ + public function getType() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->type); + + return $this->type; + } + + /** + * Get template title + */ + public function getTitle() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! isset($this->title) && ! isset($this->description)) + return ''; + + return isset($this->title) ? $this->title : $this->description; + } + + /** + * Add another attribute to this template + * + * @return int Attribute ID + */ + public function addAttribute($name,$value,$source=null) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + if (! is_array($value)) + debug_dump_backtrace('Value should be an array()',1); + + $server = $this->getServer(); + + # Initialise the Attribute Factory. + $attribute_factory = new AttributeFactory(); + + # If there isnt a schema item for this attribute + $attribute = $attribute_factory->newAttribute($name,$value,$server->getIndex(),$source); + + $attrid = $this->getAttrID($attribute->getName()); + + if (is_null($attrid)) + array_push($this->attributes,$attribute); + else + debug_dump_backtrace(sprintf('There was a request to add an attribute (%s), but it was already defined? (%s)',$attrid,__METHOD__),true); + + if ($this->getID() == 'none') + usort($this->attributes,'sortAttrs'); + + return $attribute; + } + + /** + * Get the attribute names + * + * @return array Array of attributes Names + */ + public function getAttributeNames() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + $result = array(); + + foreach ($this->attributes as $attribute) + array_push($result,$attribute->getName()); + + return $result; + } + + /** + * Get a specific Attribute + * + * @param string Name of attribute to retrieve + * @return object Attribute + */ + public function getAttribute($name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->attributes as $attribute) + if (($attribute->getName() == strtolower($name)) || in_array(strtolower($name),$attribute->getAliases())) + return $attribute; + + return null; + } + + /** + * May be overloaded in other classes + */ + public function isAdminDisabled() {} +} +?> diff --git a/lam/templates/3rdParty/pla/templates/creation/ou.xml b/lam/templates/3rdParty/pla/templates/creation/ou.xml new file mode 100644 index 00000000..6e0f45ac --- /dev/null +++ b/lam/templates/3rdParty/pla/templates/creation/ou.xml @@ -0,0 +1,27 @@ + + + + diff --git a/lam/templates/3rdParty/pla/templates/modification/.placeholder b/lam/templates/3rdParty/pla/templates/modification/.placeholder new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/lam/templates/3rdParty/pla/templates/modification/.placeholder @@ -0,0 +1 @@ + diff --git a/lam/templates/3rdParty/pla/tools/unserialize.php b/lam/templates/3rdParty/pla/tools/unserialize.php new file mode 100644 index 00000000..2a35e609 --- /dev/null +++ b/lam/templates/3rdParty/pla/tools/unserialize.php @@ -0,0 +1,26 @@ +'; +echo ''."\n"; +$index = get_request('index','REQUEST'); +if (! isset($_SESSION['backtrace'][$index])) + die('No backtrace available...?'); + +$line = $_SESSION['backtrace'][$index]; +echo ''; +printf('', + _('File'),isset($line['file']) ? $line['file'] : $last['file'],isset($line['line']) ? $line['line'] : ''); + +printf('', + _('Function'),$line['function']); + +echo ''; + +echo ''; +echo '
%s%s (%s)
 %s
';
+print_r($line['args']);
+echo '
'; +echo ''; +?>

vrPl++^xGB@OCt{3s28tPg5u;%iiBT!bk|ReBbN>8i%+AhI zmL(!Wuh-kiw%%~>0&vV6_kK`SrK%z^M$96rk@fZ06h%P@0e8r^$L=t&oV{@`ttMx^!;eosj3%6DXQkGQMNfuHv>p5ONlBqd)<&LNdid{Ff-h( z(Og0h(lnK!p&>#@q?X&a@c_(>vdn={n`x*T?n+rgua~2$v|57>BC6`{#>&cy-MxE1 zwA<|*P&4ywGVr<#BOy>%jBhaRKld}rvP{x6BZR=-z3)kuG+nO_rlAU?Jh9m?k|8pfPa7r@XaR6 z(9kdk4<3?MYe3B6mLsDN>bRR{SxY*d<&}pIzj>4-$t&PbGwT4aYJdok*5#?Qv$KEQ zym@nAetupD2V2t5f+o}YxepLR@HEX@i;FKeGw%RPb?~BToz0dDz^A|%a0nQz-|f`1 zz5eEJ;1}RG;P?7>xxwnK^FB$E4r4zaICf+Kx00000NkvXXu0mjfm*_99 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/find.png b/lam/templates/3rdParty/pla/htdocs/images/default/find.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5c07eb76dfe1a6e18714081f8775a8f43bbffc GIT binary patch literal 1428 zcmV;F1#9|=P)rArEY0$OWD%xZD%jt-I>{$@u5SwgoqD5`g@vl=J)-6 z=X=ha?}7j81ttB9o>E{vuoQRz6#fh0xxlt)G`jDTk9Xcbbl^v&?aHNGH2Tt6pbQWJ z8CdP}`99ma^_|v=ii*zx2OvC2^k^Uhjlk;7o8R2DVtG@|;zd#a>`0|sEo8Aw6IpiC z(?1;B@cJ8PeLi3Mtu323u35cm#^OcMKsucfc73(GfB%7l&9+PRS%G)=?)hfd;)VqQ zw@Y;7vI>GIAqpa*C?E(T76LidU|dm6pSMIP5Bmg362P)hRh_HXJJk2y|L{{_50C^* zY)b$sL!Xc(R7Xb1PLczr2$%xn$s~#K6rw|?r@L2t`0zfKC9<+v9Q+pBkcu&vQF@`7bq2$Sx)*xE*BVuYcG z8r!yQXF+{Ev**mgDf;N`@1(oCmleyFAv>LT{Q>8JpMMzzt^ql~5`jA4qq_Okuim(I z#}EV&%QP?z165TSPbwTccADnqX8e9HveSX=aIm1hp32Jkq|yTVSV#! zfDdp2I2s$Gd$(-f*f^0*yM~7ba4oDwQ}cjHAzz^HejmBKipNue!yzGwPAt>FFF6SX zr=lnx(y0_7zYlL|32WD^jGR2xvIH0gRPp8?9bJR*0jZGBbNhA&q9kKk7KTxzpsEN6 zl$Vt-7?0b_X5#nzaeLhO{a!LD695YVS$45>Nn>eU?YxbE7my^&qBk)zzG?QXnZb#P z9OdOx2?xFCnnt0Zp=kzVN`juA9z1S86%~~vl@zB=og|%&k;}SS+SEiS97YgD(uzV? zPp`YHtNRF$k$_Apm2r98B};<=uWSN!weu(xvJ{I&^kR|ZxXMI+gxhzn(RS@TfAw}U zo{ZCaxq}&1KKgnSl)4=pKlYpU)BeLs%b81UrfIeTiew*%?$NR2ibz#uRWg|r)~;Je zEz`+^$mm!BP-YeDUSCqeo9%KHS?M|G_lPOF#-3 zmjDaUbiMdMEfhACmxa8MNQ8jTgK4NZBqvU}1li?dO8GQGp%7+KqrLr>arw&i{Dq6x z9@N%Wc^A}Id1A5tPE9k<0uO-%kbZOyEMQnkWdfmqzo9G~a@N$$MU)))!_x=^!kDJU zy{j-5CczuDe(E|t!<=(^bxi}fe#>Sjk88meEpcklj?sv1Y_xX_;zFo3&> zk+G)ovcSw0E0>E!El*-7&hKYh^zXhukoxub`PQDk!6W&Cb_i$(27siln~BcOzTujh zNbS(jU^Jc9zXq~^^<=aPfD32}hl9I!?s$9d-Mg`T>-Em~Kz#TD&<2bGd3${oo5TfF z0keP_``tcZ1Tdb;ZYFcy3#TvjW(F zATR?6*d!^NIBSa*?Z5AlbJ@m}+4Bh-V?Iv+CS%-V0}b1OZa-Vk=@;w|lkKtg54}%; iM4q_$!g7TnZ3{T_&dvjw6Tdj$#2e_`Tgee zyUy70`1+}*r{^PCmOB_@>zy6#E3dxt@_9*$A2UsJn@m%Wn?L=;o=|?&`~iVeILK^(*H*ca+5%8XD$3 z9*-{)iF`|nPyBxWhrPYM%x1Ge*L6grQA8u#(A6}W>p0g+dzhKKhd|(A>b-NPo7F;& z-~Ve}`*SJ0v9J&dyMOfB*ASQ&alr?c2s@pMP3=_Uzlt>8v8Xts)GD zVIm$+AhfXtJtv{nqoKWl!)4dNjS?)Qh}-coNNu6nv4`WWCaP>2fA{h=%skkv zryjA6OBdhYy86u(8?+qbrV4JAP{?Jm5spAs((p7p(Aiakre-%Bj%sp1jvz7;yA$() z5W0-N(d5?n$#Y+(Vu@^vgNhy<8Sy_2VW@C9Oq}d)0?Zm@vROofX{_ZV zfZM{cZYSDWDf)62PH>(_GM%9e6fVixyW|aaamnU#O|SL7^v;Q6ZH!UGmPv3DQ9EuH zMT%&u;pl1v+Nw+>H*=W1ub}%x5EhSPW4(yvmW-jvCHNP&`v~b5-2s*3$AN*LEoL*h zXaWTAh*)$l9Esgd8NJY=qmkasQwD~yhGHQNIho;i?uDpK7!b@dn98yB+t|#!lHBE- zz}zfUgq%>sLc}wabwzTZ(^PQ8VoqqPM*Xl*C}{|8$x)i`-9xar*;6tDzR_usI>G-f zgK#7wJ$MGw5a#fDEJSx)C}fH#<~gKIbv~O41L#c3~P3#6NDlZ>67f& zTbwC!i=dTnwVs-oS7;QO1yg5O3Q9ub6D+X~!JNaG4J=JZCCL(X7ELSfm1t=O51|wU z9MpEE*U^mA!DM_joZ!@}>3wWvDh>{sZaf&DUN|MEmDa{uyTe;gBW0&TuoZKL zSjvf7Zv@MW^|(1QFByjQJ1J{O(&ceG0Mnj`Co|to2Uf4%99vW;=Y#E~l2+f{7+hxqnXqXEu`$<1eLiK^?NiaPtuR@`uPlR^TU-@Q(zg!<ZdP zC2r$$UJqQ=4w_#uJNXD_RNNZeKzJ*5gWlXGc}TMUG;o<-xx|aAF}xIvUm2QS^WU0$ zRJ|Taw>NoQ)tz29ySrM4!QnY8lg)ii_9GnJY(i5b*a|{viz&fDK@p;)lY?EeSqdF8xKuX>ow^r?;1CFf z(zR|ajxHHoN*y~$Kok)|(xfU$Ufz4TdmVg9W2)lN@ATui?}z*SJNQo$J@Gvt2|T`| zw+4I%8h}w9U^<!n|*(9aPSGJcR;dQt!9$R zQzhr+``n9vO!`$2)nM?+y6vh~|))-^1+*iGjcDvoJl`#fm z45?I#v9U3<)_9(W)*8oga9#IiP_x;*JX8wHvJgU0tJP?=T1-z*<2VkE<6zr1wryjK z`4{AQ-tSVWL?jX+6bf}=OG`^wmPMn{!1KH==;-K(dcE!d2Jkx|Q>)cJtgo*>4~N4~ zrIe!6-o?em@cjHdo12^Z?Ch+iweC!I`SkR3J6MswpklH3qgX7y0v-i>Ct|VKlgY`+ zFO^D#-QC@vp!2P}-<4$@3db%707*qoM6N<$g2Gft=>Px# literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/hard-drive.png b/lam/templates/3rdParty/pla/htdocs/images/default/hard-drive.png new file mode 100644 index 0000000000000000000000000000000000000000..f3da3d2ccd0c4e6b4eadfc1dde88701435fa2062 GIT binary patch literal 1308 zcmV+%1>^dOP)`DgOWf1gS|x zK~#90m6c0xTSXX#pE(y_Vmr3u=29naLfxbn)F4tyl~TGaD}4EwTrSSi!Pc5HVs5c8@FkjrZ|`8<|K`8$3D5(KK2ZY2IQncfmfQv=$v_<`M!Ba zBiO0-w$_%f8+;9?cv)4;vTo;c*5^QNd(=y9j~z00%|CtXT>shg=T291xjdRCFbspL za0;`gUSILP6yINjZGDwQ%fZu~+flL-KQz$h@j4XJ5${z58+OPE-z zX}pGy#pE;6x#!GmxXEpm7`}CnM&|&>dyaA9Se&ErRwf>el1L;N8yf=%0Ih%r&~~4J zKq(e-DYn8UiRm#0?%rl9HH#iPfnHMts}Uk?5l;1;;P{bdW~T1(@ct0#bQ(?5kW%V^ zYujAZ0|a1Ht2GvqDeexA;nn@q3Zm237`p$Cielzx;cHmpZ@-SLXANxP6bUkL_%)nXA_K!-skbtc`6*D zwKdJjlPAe!GP@JH0{}p+RwJEG6OYHac<~~BuaD*BWs3O}0Z*Qx@ma7t*HKlzX&@oplK z2-T`XI2_{6ogvoN*7gSoA&5q!n1(@WDMdIOCKL=2>^?{Il{38UauEtQ;qiFUG=Z*b zbai%;NF*qi%lo!S2_f+Ld=&C|%9Rp@f<-XcNNcp6#f5p&YtNaPnZh((#13^53WZTX zLxT^;arOl4xCcrpq^wbuHFQHqB0-0OmFLQpAGmz^63=F57#{wEWHL#iSfE@f5eWG3 z`~CY?@HYq3G|>$mm&>G7+M-h4BGMEl+!Urzu*m1L6pI$+atV|RA;ilzd$mX@g+kF3 zX~OMxqiGtoT8+ua4{2#Rz}Yizva~dZ?((1n*mjXkP3PI%9Hwdhw+aeYfmp1ASjQox zlvFAeRwKDz>E-ca0bM(azhCx?X zCk+h^WP7u0Zfziy1SHvfmIsLkRLXlF@SXs}FnIXr5gxCHo}M1uE)%cUM{|q7=l5gV zMK(6p$>y@$9=L^F+@SCEH?ZyT{Q*iT9LJ$jDKj!M!u0eM(da?i+uKn1DC@?ws zgk*AxN~y}JzSBe^5wh9r{(zO0m5rM>Z~DAmFM&XSrKKfCNAF^q2AZxRYZA+{ux%T+ z+f7^BLDtvT$z(Fvwk>(_$m~#kzJw6sBTdsj6GDW5e`2AO+AD{uKAuv_=TgewfIopD zU=46~FZ2R2;4l!b+rYmEeWyy>#uGpRsO?^;13sW}8}@$yiWe=~0P=tXsJ{R|KLSzf SPxwOs00008wQ zK~#9!m6mI4Tt^jvzx(p8?W`ZM<2X%>A4%&+OyZ~&;Hs^Ks6b6xf`Wq5{sDyeL4Op1 z_yMSdN7WS61|lJ70il9gMWD1zDhP$Rv~KH~*p1`HapTx=yz$yzuOGW!@8iDa&b|EL zx>!M|%8{;SG@5h1d*+-uXB2KtlnwLXRuyiRO$}}PTB<5zPnDIG-60*b!gU-~Hw@p` zqWN@Y^_pv2k1xirOaW`R1ZZg6x2JYX-E;Td)7|~hg9oUqsU}uYNoi>*bEzfP3Kq%a z0x!REhSB#g4z6XEA4|_(8wTV@0y;j?^Yv0yKKM&x=qzPT zG+k#UXHs+}uJBMbm8w`7mhG~A$2L0mbQ!}J-n+kOiBm7oB{15U_PX zjm2Vj8<7Za42)1;SIba;A7k&Fpt`Od#0r7$BC_vdxXaYnYWT92=Z_!aiRVu6%NI|R z%bOG|7t6AVltch!FHiwwbwKev*<4w@1(_={HMfMKsA$pKu(KabC77@McJ_np{t;@YydML3k3+lW4j| zHkZej9zhUzfDWiS5cr-OFJxEt=_L_tSJ1Qn5W1$J=_fS15wd|R@Fq&@o|K}b;HkbC@f6O z0AoNN@YVsN>FGRKB++W%|%T6ooeTwh=RtyC(t zzWz7hPhrgRKqfSr4x!@a*M{;DFAfce0MT$<_gIGB+F@hy!gT*1`8P&w9Xc2aXUzZr N002ovPDHLkV1f>g7GVGY literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/help.png b/lam/templates/3rdParty/pla/htdocs/images/default/help.png new file mode 100644 index 0000000000000000000000000000000000000000..674a8e92b7f20890210e7f9e52c3bc44733dffa8 GIT binary patch literal 738 zcmV<80v-K{P)nJY(i5b*a|{viz&fDK@p;)lY?EeSqdF8xKuX>ow^r?;1CFf z(zR|ajxHHoN*y~$Kok)|(xfU$Ufz4TdmVg9W2)lN@ATui?}z*SJNQo$J@Gvt2|T`| zw+4I%8h}w9U^<!n|*(9aPSGJcR;dQt!9$R zQzhr+``n9vO!`$2)nM?+y6vh~|))-^1+*iGjcDvoJl`#fm z45?I#v9U3<)_9(W)*8oga9#IiP_x;*JX8wHvJgU0tJP?=T1-z*<2VkE<6zr1wryjK z`4{AQ-tSVWL?jX+6bf}=OG`^wmPMn{!1KH==;-K(dcE!d2Jkx|Q>)cJtgo*>4~N4~ zrIe!6-o?em@cjHdo12^Z?Ch+iweC!I`SkR3J6MswpklH3qgX7y0v-i>Ct|VKlgY`+ zFO^D#-QC@vp!2P}-<4$@3db%707*qoM6N<$g2Gft=>Px# literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/home-big.png b/lam/templates/3rdParty/pla/htdocs/images/default/home-big.png new file mode 100644 index 0000000000000000000000000000000000000000..d40900d98abae5328e1c2700891e8d7e902097a4 GIT binary patch literal 1084 zcmV-C1jGA@P)WFU8GbZ8({Xk{QrNlj1+3MgYKATls8GayP~Yjt8ECu(VJ zZDC_4AX9W@X>Mh5Co}I@000AjNklFZBx>O9&-t_^ipF8m|jYaF(Ot?Kvbn*dD~bk zMX%kp_uYebRjpQSlN>rQush$*{O3P2-)soKoCXSgHZEU&pBIsP_rAF(^atPH-zS|; z1K>CgSFc{-#`OBZ@z-v-Ri~n#~qxPiGk$9VPR|5`Ub zPXz%0QmGWGss>nia{taPS{i&Gh1+-UcZ?1!FTdUWeqwThCx3{80aR6`*=*uCPS+t4 ziFD6jdj(!j*;JIU&u?vQz5ATWoa9(49t1E=lUOWRyrg z-~R-_vP>>qSnN=7)c{3{2A`m&@_twMzgz_~LJ$TrM|o>bzq40D4Q)+XGo{bL-{}KD)V! zLvXU{Ppbi&qIOt^dgGT$B?LjB(P-c}4yI|cxw%Q9@aRQX#T6gG!NGwqd($-0G>u3k z;$!4E4zXAaQ50ENSm;mGC%zKdw#~-I2BIi3Jv~h}n`LBVq|2yR4-XG%wOaqlBG7|! zxlA^jWqW&@+1XiItrnsv`j|P6Ln4tN6bd~*z|71HTU%SKtgH}^$4MrW3=a2w-h*GZ?-G@DHf z!@x96EX(STWBn6@s0ZK$k4B^Dy3Wzj5td~k%QCvIqpE5*ux;DD7dP~842}cT>vbxX z&JJlb8Yqf_q9|0WRb*MFR;!_D8ufY|S(ba_i~==48C0qSFavz*9zL&r;{MOaykY5; zkgEwG1;pKV!EL6f{n^x!XSQwL-PNVi zbh9XpIoc=^3TZ^rX~rj7G{XbEwTj!nKIQ*#DO;{R!07k~n@rsd9DI7q#us! zOW)1n*K2t71}#(wb}}+jfRoF?d7Nb{OzwR1?APj*jGHAd3d!tik+db6lCm*hST#rnbke~oY!c?AEC1&iS2}VY+x%s#tUQT z&t#Oo{%xlxuKI7zPTwHjp2SY1P<5MXGsGXi6p3#broDR&$#jZ~C7pF?hrXDKrfDde z!ED7MH-2`fvSH|rq@H+mSD6fxqti&QjMAoa>E9AY*A6m0kCRG7u*?7hDUFQYL<$Hy z7K);fomrsaw@#=4nM{gRnSQ1wyErp>5&P;4-D`(v@7YK)5ylFd2q{sU;JQU@+eXvW zyEy|_S^bF$5DXfuUDHX>vB~Aql!^<)*7mU~VPgjkgre}x@$a~B;Ua~?0tXK4qkq#z zR7Ij`%TiSD2})HJ!eN_0po{!}5n}NuAxA?6|Ep{g1Jfmm^15}3&6=a1dFc^h3_rYQu=9fiCL zkm!bi?KoJLjiM-b$=t6bkAC)EVQ#MY?(e70eSLi@>jm3Xq!iq9098{l3?18cutN?~ zQ2>GOH)%Fo_eVSY@jGQ;@BWXDGT?iA(j94}6d(jrDo7#G)BsWoplUw1@W*8uUY)tZ67vg7l)N?^Ge{!oFn(?3*ojl4V~%Cg zo(j?18PAD_AJo1Nzl;P;{g9~}!(m6q)K#k0#;2p>H-G9#$4JHvETe5=$Lp^YR^t8# X9hhd(3{BE{00000NkvXXu0mjf`NS5_ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/host.png b/lam/templates/3rdParty/pla/htdocs/images/default/host.png new file mode 100644 index 0000000000000000000000000000000000000000..87426e700532fcf0bd453f520ad5c5152dd7e44b GIT binary patch literal 844 zcmV-S1GD^zP)VW z`~@jeCAM2dG$|CagkZ6=Ou)k6N3byO?99$MbMHMj#eBHBuCNchyzk59{O)zL&JFV(GRACgZf-sWY5-CHAPmFu^78WV?(Qy^mzURyL<9r`0R#~M;qjw~ zG+V9DfiHlc0fILRlO*BS?QNPG6Aq0qJv~jsM#Py>h=!S*oaDiS0);|>LLtu|&!5w} zae2fISsCK1wN=6(;Mv|wviW|V>&!1j^n$5$*Vf2=l>B%fCSxKJ)o+1 zuUKnws(4ihdmC_b-v! z0pxPIPYwL zl3z$uQ5?sAcX#ZX&g-(>IVNT(&L$2r(rGCLC6xv)r4|^155*Ss&>yH4MwE&5Qb_vP zLp|sqq=Lw3D3tY5locaw)YQ4u?YiANckk}q?sj_GSlf%y7tX`?aL$JxpK~yT5d2T% znMKnRBs-h(hLxMJs&xTM=KT`-;)yQAM8P9+YxlTkb>Ff9SZV_EAZIsAcP>bpu=5*5Q5{k=dSlT zc;VQCB--5<;#BS9rqmf(`z~VS^8&7NdxK7PRAOfV`%f1L35aod| z()eh8P2hr)Id2qWz_*-OuGwa&t*+EH2RICrc>voDdIPB>Y_ng(R=I02#>T~D0+J%H zXmvIsiB|z|0FnqG@pTxAau+(c?%!#f9BJ9tbh?Pl!~NcQZ**nG_H)eg0=9H}9hGO- z1>)dTb*4as5a12B%$a@9xu^Gghd$0Vo_gGp7EN}!?1oEmL|Hx!<^XnFG-vM>G+k3? zb{WSk-ep!jZ`Ine-eU9X`fQ2WiM;QpGrLT39+Dy<-}#uxiMsOYF>TjGNBVv;?93r# zCH<8P+n!extF=QKt*)74eN(L8<5*pqeIk9hA#Dhdiu#D7LrIeL3LI(~`j<))*B=EZ fuRPo^;9v6xrt)k;e-NK600000NkvXXu0mjf29{X= literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/info-big.png b/lam/templates/3rdParty/pla/htdocs/images/default/info-big.png new file mode 100644 index 0000000000000000000000000000000000000000..5a2cd96561a20969638d15a41f0793c5372f750b GIT binary patch literal 966 zcmV;%13CPOP)`p6q|=l^@Q%_i>9JO0byUz;;B<7}|~`+1^D*XJ=D>Wock7zjAeFxGDjSorj;e z{N_6!bh*J6^2IW$BBB@04|DcZ4`)yHaP?;DpYM)PM4J<|CE}r_%>1((#q}#d+4n;C zLluo4>>d~#dRmVjVzs!1lxkOLsFb2)IwTJFl59V6LJt^&Km;%<0d=u>>ub??8>TJx zmG5Os(9wO8#-_+=APUq0n&AV2^$kZK>wbpC+-6mQ&%XTjp^&zYqZo$K0)#w(=>ueQ zYw}niU{u|B;r#HfU`i>JBn|}q`}ISlqw)A7w*_58GXi};y$8^IfY224wd!s+;Q=s= zdXValC$3j|pmbd~rEng}gHo}85N_5>QTeYl&1Dzoq+O<=-q@G8a2+zUf8jdT0$};Y z<^$YvsdRCA;uf(3jp&-TCvl||#X^pmiQCxb_HSNKaw|O$nT7kKm%qC-?{4O32-fWh z;5s&$nF$Kn%%qfJ#;arpP?blm1NzUrJp9cYZ+{qGEJ`ffMM(LdC*LX+N#7gi_RrrJ zm+s&D6u1k_0&9S~v)L`rO{Er=S62Fak9P-S%`I4tKnPhi=3C_rQe)Q{zwy)hQhMqO z;7?%D6L)qtJLTQ}%!Bmo=u|3ox;~&{P2n(+NCc!{+q7_Oi_P^_($jYszd6F)Th}I6 zvzgC=Pq0UKU=irvm+-4hG~jg{r^6_M}+%?e?; opT7#-v(qc={b3=!Rdaa!4VPn!l#>T{V*mgE07*qoM6N<$f`q}@Qvd(} literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/invalid.png b/lam/templates/3rdParty/pla/htdocs/images/default/invalid.png new file mode 100644 index 0000000000000000000000000000000000000000..1c427c04cee9218d5dba5fc95c2d45d16641c82c GIT binary patch literal 692 zcmV;l0!#ggP)N+@YjJY{101#-I{SF?5i90BrrjbLi>c(*kasZjOX=$m;%=I&cck#@>f z3+dbQ3_r}$)08CBYA}(qEMN+tPB1>!86rv{k`O%%h^G#7bY`CZd++Gz=|e|i7zPT( zXr~1*-bgUm7x4&?SZ%JoL8!gW*A*;(_!;5}g>qX--griHJb}HE!(Pb|>m6WldX9s! zI6}g5NdPQ263o5cQaAzvsSyLh{00006nzXdEwY9aAlarX3n6k36o}QksudlecxR;lgsHmu} zuCBJWwwalkot>Snt*x-Iu&1Y|qN1YK*4Dzp!lk99prD|eo14GCzq-1*p`oGG)z!PZ zyQ-z%gf8z+1cac5qobqj z?CjCe(a_M)E0p>R00001bW%=J06^y0W&i*HrAb6VRCwBD&)XV8Koo{yVoEASDWpNh zprV~k+4 zFuhy}>|)2|&HUHTl0vy2h~8_7C_m5Ls6Zqm2Ln002ov JPDHLkV1kZM49@@n literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/ldap-alias.png b/lam/templates/3rdParty/pla/htdocs/images/default/ldap-alias.png new file mode 100644 index 0000000000000000000000000000000000000000..78dfcf833710eccf9ad7b4e58542e23f6de06908 GIT binary patch literal 440 zcmV;p0Z0CcP)L-G+J2%#LmLX*2>aOQshrah+ttEh(>%w zn-qe&D;gh}Wp?&j7~_L%valaaHJo$ronZzhXLic|)JRr|WR*y>b(l#akNpSPs%<1n zoL_B@Tj)@;xUj_L{yKpFU!nlfdO&DHj5UOj#@GmL20Xp`NUR9n0(7J~X&-U0UE?Lt zMIaanZtuFZd#6}2KfQ|(6XPfsSIuC=V6EZ0d&bV%Eh z>K%Zh^QBD9{(*tO1Gws4^4|X#3*-yAL>Pv*Fsb57b(wr2Hx>r)hLWU6QsuOfa;22* iEo6be1)2VXsqiQ56Rr2?(cQb|eZ4?@zWNh`LsRH`%@8;$yduis4c z2cwD6`VW|xG?*BFs?k`}A8MnR*iz+DYFmY6rLd*@fbIkKHM=`I_xe2>z{F_cNj`J# z+_~pFcg{U$1pPk~>&xu$TR;VH0iMr-5Qu{uP@wRD{eKw;vdA8A0Bi#iuuvGVC_irm zy!JA<0cA->#3R_kA}MtX_t!xh6c)hq?lv^s7!Ly{_+(>}iYktF z7B?L`t5U@!xw{{El{LSsW_PRA{oLst&Zg$Z#l*C}GUUrwacQwh~ zTMwt~vEG~~I7=oygzz%JI-Fnrk~#3Ij*Ac_jXg!K*0xNMt_i!M@`lT$ju%P>mWzK8 z)xu4EwF5-Di=<{F*?LCL4ye9_@8$>eqa22m1r0vP2Eb|Z0Xx4*sN7PsmDGl#bdZeS zu8QL?X2fjS72z&(Qo!<1D1DdO%6cfPbV^DovbI_Zr^a`f^N~r4t7&L(TKGWO4gsBo z@hS2(nT+1b-CJuYj*r@&dFNSeD*iRCm_Z5~D|D!8i0VrB(5huoET4y#E{dw#$e3Bk z;Uir z6OG`Dg+iw)j~sK?xDE)X<1t#3#WX(qDFyu3Ncy7m;qp%E8XTg0HY-Fj>G1gdey?f1 zh4?&Z2lFg^AG`@h0hf!J^80T`gTdpnqr)E8b+Kk?By25YK8TZX^G_9?PN;6A#f-(o zJ+D`N6$*_lOZh!w+W_ZyjfGbLpXwW+3P_SNnDBU}9*m9cOT}Uj$8OwE;R}~k`n#EY zekwj=WX$m`kx0{`&!>&}e39$9+!yN_OYr;;+3fRDjQ4^{unTa(^MUASpA38iDH5b< zUc7Vy_X(81LC}L%;}qTo3t+R&@odu^81V`7AB&4U1-PsD1slel$}L;cm#rUxP)&2s2(5Z)4dn3#M0000ZHQ(#!7&q(=@Mp^Y|DO`C7S)%lEvYS znalp|$ChPTNVb{q0~3?Qh%z@B5V zYM7?nBeb2!IjL*9m7-m)hVFcMgraip*1YT;OQBHTjL)ZWzA*#6#;mxw*qoSXInmg7 z@zIF?nlUMn=0VNuLxI7MfQ5t-r+b^5=`p=A^St zO<%oNU;nPfWU{l7i3t9@c^%c28YDe!T~t2Z3B#NMoVaL2hIJP^SYBZ&u-j|hUT=*p zGxNqnzu&*cEXsz|<}+t5H|(s=lQl<44+-f z!Typ1sL0ADp)lsOScDg1v7D=JcPyxBy8M=FA{k^mZuB`>AfTY4LV?k!VeX-f($Xr7 zzxX*09c#jejeo=MVHge7;P}zEVVw)W8jXT4E@JS~WdsNAFvC+dURH&k;_#4+nP~}C z)d~b*1}!aD;J$qqM$1b$)e=Lk(~LLE(vYI4$f~Z!)Wvq(YHfu|*(Xq#PalX4atj1R zbp2*RXZMd7_fEiINQBw!Bu6sx>~`eYY?yVsalf}0_b#--5RSk|q$*`kZAhken5Yhm zdYxAt=^bB@ODx7PKOcoC$`Az!2?=(Hk`X&xt`+%`Ce=Uya)io81Z$D0KmmOoCcHv0 z3&G(1;M7#VMi&BcuaGk$6h#9GQGlkYoJS*?RhHMQs7LbzDfiI#9I!;YZ8WVVREptn zu=}q&?UEpf6`JbW7Hi~zkvNMghlLce~yEvag_%j4;~IXyF6=5!Wh%=jjl$I}tIH~PypMG1D0 z&5S=3YVVntm=gsdKZmZTq49D49r)Dmz&Se9!(uFpYTw?e`g)XfeDL$?n_k4U{CwiUm_U-$T(p!& zNGJ3VW(g||pglB_s2_UY^ff)}1wxpxsi8YvRn4aCHyHg3Jt4T5K1yO300000NkvXX Hu0mjfxy5A^ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/ldap-o.png b/lam/templates/3rdParty/pla/htdocs/images/default/ldap-o.png new file mode 100644 index 0000000000000000000000000000000000000000..5acff48dd43d2069a1244921c49b77597f448e3e GIT binary patch literal 918 zcmV;H18Mw;P)V;Q$C0r1rcP3tVqX{>fh%te9MTkVQkhl$z&A0{D;a1v>wUw>w*V>+b?DU+|^Ycax zL7(gQ$$Rs@63?IsRdR#{9F+-}GyT z&wl>J)f>xpsbUD*cEr=v4I!rQB<$m7-~STu{-^lG$qPR$J+#yKRvp?`$O3@%jQ3t1a3zmA5BH33hn#Y8smA zK~+@@!^SjQRCI&-o=1zWt?nrP$P1^3dOFz7=)_ZHKtk8|$QE@Xp##`0XgM;bF>!^6Dn(od&-xr}=d;LC~);)PDfI*2K0U zwvl6c_CA^79{Jr|SrLe))oS4&apILR*5Z%(X?~0CtdE)bJp%1sCdP*m4&;h;zPfT7 zAspHRD%;tj<0|DgqAkaTgo~9}lDWkslB8nVHZB?Bsa?tygT1EB)tigN*ApB&($7F| zC&DbGlvHBjR#|`VgH(EpYd04#gdbTJNRotapUQ(jGTd8A@@TWbt(hqMLt#4nZMaoG zR(X9^0@M?4eKPA0559W$_XoJOAYRRlrYb113&#=2E*HC%2Bu}v(;38e6!d&7ky*Zc zN&--QJ!i&FfApg%WS{C5C@vY*tspBd5MVSdnwE|2I2b1EWOI%l`}Q2x&i67v%c>?z z`D7~GGxqvG-*cYcPCuPtACZucaEF&rz(+9P!=rl1=XdPF+O>}zBYpYbDFfI>aYfHY z=QOqL&|v>?Ur$$;6bXe11_F58G8MhTqv+i7=F)fPt;UnD|M8^%xuqHib)Os6wLUw93ojc`_#p8x;=07*qoM6N<$g87rh!~g&Q literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/ldap-ou.png b/lam/templates/3rdParty/pla/htdocs/images/default/ldap-ou.png new file mode 100644 index 0000000000000000000000000000000000000000..3e767c7cebd18c77a3e8b98b3c5cded70444d3f5 GIT binary patch literal 1330 zcmV-21DN3OWEw!cV%(OGp>D-yQ+vktoLd8gYlaqWmH{W;8 zdCz-Z_;1KSSPd4u6Pgt2y{jy!t3fpZJOtzeE zR%CB~=fEo9FDJx=_}`F#1(U;NV%x?CMcv8;B2-ZCw4KBvkMj8O&1x8^aJ4Yx>>Pg1|RROM^*g@1QHTPCV@WE z&%wjLCf{#}?9g%?12Rq$dz>rj`OWpxdv9*%n?tWtou8u6duvAPRxV=u)63}U=tR%i z7&!^kFhSS=Jht5LD-TK!00F?~&@GLtMXsqVDH=&>i>k^)p~vf&Q&nC}cVCpjSb}Yv z))FWP(9)6M!k>fGhG*e&xk03`jZxwQU83RSNKaDZEMPie-K^Z}aTk__iqw3+m)!@y zVBwsp?0I<$TQ{tsQ1!ET?ksLZEUGGMIosI9*TG0YfkLP9JV3rF zq4H3$YR)}1m1UtKwys}8k(!U+>mifVIq=C*)^6EFP|c@q(Jbc8oJ`$)bCKmBXa0zD zu3gecG};_lx*6Lad0dK8MQMO zZ*9NO)f*ugD7ZQ7uAT^n-4d;p#lRH>9Q$`-2@E}w@06DFIqCh^q12IlF9Y-x2o!u8FOO_@e3TS|?*uz7`u6T5_ zXRw>a2qyKmBZhksKwUb`{HX>Xc4)+Epek(RE9@Xz{ABt}40 zr=z+Y(FxoR_u27*Mq95o@3I9aB&3Wf1eOqtST;kx0CH*Nm{Bm6G-rSzz>+{W!Rsx> z5)zV(jzk0)0CE6w)vE_=MN3^OcDr7YB=MOA2~&U&fZ|5DePd@F^(td>$eEy9UMdJv#unm9pkA^qOw+jOp}IX*wJjW1azWP2oRcN# zQwli74LX^CfB5;_Q$4+n_fPsWFU8GbZ8({Xk{QrNlj1+3MgYKATls8GayP~Yjt8ECu(VJ zZDC_4AX9W@X>Mh5Co}I@000AjNklFZBx>O9&-t_^ipF8m|jYaF(Ot?Kvbn*dD~bk zMX%kp_uYebRjpQSlN>rQush$*{O3P2-)soKoCXSgHZEU&pBIsP_rAF(^atPH-zS|; z1K>CgSFc{-#`OBZ@z-v-Ri~n#~qxPiGk$9VPR|5`Ub zPXz%0QmGWGss>nia{taPS{i&Gh1+-UcZ?1!FTdUWeqwThCx3{80aR6`*=*uCPS+t4 ziFD6jdj(!j*;JIU&u?vQz5ATWoa9(49t1E=lUOWRyrg z-~R-_vP>>qSnN=7)c{3{2A`m&@_twMzgz_~LJ$TrM|o>bzq40D4Q)+XGo{bL-{}KD)V! zLvXU{Ppbi&qIOt^dgGT$B?LjB(P-c}4yI|cxw%Q9@aRQX#T6gG!NGwqd($-0G>u3k z;$!4E4zXAaQ50ENSm;mGC%zKdw#~-I2BIi3Jv~h}n`LBVq|2yR4-XG%wOaqlBG7|! zxlA^jWqW&@+1XiItrnsv`j|P6Ln4tN6bd~*z|71HTU%SKtgH}^$4MrW3=a2w-h*GZ?-G@DHf z!@x96EX(STWBn6@s0ZK$k4B^Dy3Wzj5td~k%QCvIqpE5*ux;DD7dP~842}cT>vbxX z&JJlb8Yqf_q9|0WRb*MFR;!_D8ufY|S(ba_i~==48C0qSFavz*9zL&r;{MOaykY5; zkgEwG1;pKV!ELjL%;Pn)6~ zp+z7;99L)q8&6i(@;pDhvS!nc7x4PcUW2hXn?r(7S4e~~4wNS)&tLR1=iDr~xz%s_ zpCYEMNeNI9QX-`U1SCOHp6#ty*-X@(cLimD^%ZdQkQYW-ZbURRu<80K^-R;In0T@uAMdL%H@{m-*2~J%XyJ%VN zE@#($GnW&I^uH7h;s`cp6e}0&OCWbNaqnkKr2DFgq@H3 zGts7?ud1dvE>`|~(zIvbA{ONS6{SQaYpg6)B3o60TPim(N oN=PK6hSf)_xH~Spv5tS$Z>|sW3LxX?DF6Tf07*qoM6N<$f^GL0UH||9 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/ldap-uniquegroup.png b/lam/templates/3rdParty/pla/htdocs/images/default/ldap-uniquegroup.png new file mode 100644 index 0000000000000000000000000000000000000000..c86d4a133b0357c7fb51d42a9cad7f9641a52246 GIT binary patch literal 1356 zcmV-S1+)5zP)1$yWwj7!DoX?^F;SxkqLD}tjFI?(XxxGe ziF`no7>uX^qftZ9)L?=bC7=*dOo5`5Zg#Py?X)w~cBa#Lv%Hyi{m^MC7>Or2A8zis zXZ!!px$xg218&us@vf`T)jK868CRWZ0JtAW2NXcM!+VrF+~orMa@{%4u3W!DExx;0 zkxjDI)7ifg_&cRylKyXzfQ6;*Jh64d8d1Gsp>P#s3rUj39n}JEAPcahs{EIjZae1y zJApF5S(NYMnGH4c`G$CL&r$qBpxRB$n_*vSmf|0&c3E%k)(^M#=;weqU`(!Ja?f1A z?j@_%Jw1EDQd_{+Pj6>475OsHKlK1j9ld<@-6>w%u?&YJ13eN#Bs7lKUhsTS+qW^K z8J7VaNT!0^be@x)xzl1!+n;7qD40=6OT!fgy&eE;Rx`_&%;Mv{HK?i`fj~k+j|7O1 zcsO+A=ivLb-W^(ulRzXD#2D|Dv~5$3^xm7>`R4HJ6sL#CwBE#M-HJ-KKe>#q_DP`ojosYX3Jx%$yDtuo?FfhZ<3;dGA`D4@%70jw1^u+S-{!)SZp{<6Oft8JPnX1 zD3tGV7FEnFnVRRyV$1r~WU1-ctrjA&I0rvD!P?EcaH{E4SC%t>b}7~O%te-+{QAc* zb!}37M58rDOEY8hBac&2D#&$`ZnMzT)=gQ-R16^qjD%V2ceARkl&~7+t`aw+;RwE= zAZ5h`3b z`K9$Ix_Z4h9T^i~clCJD?a5gw5cNrP6iVljy)HEzIN)q=d}5&CI`u;uag&Jw|1hmr z8fd<9je$iSfr5p*(`E`l#7aqX(_hIfH2w3X#sCI{C!_Ev8K>!GNmgrB|8%UBxRMVeGu3dqh(N_ zL?CSn=mxwsvtYvJvkExg=mCK_)%)l;puU#Ird@{MjD!?P2tnI^g-Co1u#RKX#uf#T z5D?Qr7w~eEXTs*WMlX^~90@Pb55xfEqE`-EL;gGtm*;|&6DS-7{3$Bl`jcE+UQ}qn8bgT3wWO%gow-cO%B1CV z9jB9m*ybpX0)ZhQVSqLY(@QJsx(2U){^>yCq;Y`G$^LHH=X1~Z^w!^7_@#g!Bq&ca z5;gHqaEW1v8A#-KdmfS_2{CBN{I=bi!6n7Q=~yCi%_N&V#(_kP+vFd*mKm&c?b7!E O00002-8;Lr;ae?SlX0o?d^xWXUM zbK66O5SJ=ZLM5VBDoxs$lGyR$c)c?_!@+iB5Pi~DPkQq{@6EiKfq$CVDJubp}{;aS!@;H zD)60A;^vJjMrN@hjs#N&S2D%rrly@KMrF`G5P*lksh-0tQqZ}zVf?qBWgsM_3;Z?q zTw0ODT0rpp@bk#;zOFhxumZ4v34R|Fyjy^ax^EtLUgxwA@*XtbLWe8AcogqY1V03m>f zc2klX(OArC0T+qprNm!%*5Rd~_Dc^yg7*Q5C(*nFie>nCI0;V&u3@q7>w&MoP?*q@$#8Zc8~1iy}m8Vs^(zec(h+qInSg0l7oSxtZMf9w%9$g zkAai(&e}w`L)uz<(l_Jog|kdkQ#sGuV&+dLHOCXdlm3NqNbQ<-!`seQzlz@7FCPM2 zZ08?H-Ds~4clBEF8~aCR7zMV58yhL7s~=LdK6c(s`zQUw-vRXJ_rCc8AYHx4WNP{I zAg4I$;@uv{Zx3l+&MB^aM7nX4>znWEq+#;2;v@&q&Gr2JZzRgIe)T=L)aI*uU!$rK zb(FF9#}J~FBwa-+Npn2`Ku^l)3>6X~1UiZ-t)-oJx!c>~m!0QqzZw%qF}AJ=&Jn`w z9%WTos>)*ROd)urHf%q8K^-L9&t4$4Aq0P(vta%KGq5k;G0U|e00000NkvXXu0mjf D-(YCl literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/light-big.png b/lam/templates/3rdParty/pla/htdocs/images/default/light-big.png new file mode 100644 index 0000000000000000000000000000000000000000..d22f5e954d3a5af6d5a7ba0649b450130d66423c GIT binary patch literal 1950 zcmV;P2VwY$P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOb@ z2rnlDn!4ox00$mPL_t(o!@ZY(h+J0{$G_*^H*enj-rd>V*<^Q{MmLE~)4D|zByXwxd?bj6bN1rY8aU>ks*l~)&; z@t!gKniuN*O*fgq8jRX;8mKr=644{ydg8}pgVzm3xm;&saB#4|%$S-w@4i2A%9)sW zr}*|;N4K9oed^f@wdflF>emd=-|K86!Y`lr!B5j<{SWCGe9YC9l8I($NqNb zU3YgBi=`x=w{Ng4(Z$TIIMy|-Is8`v4H!|ZBc)_>c=+bhy7dG8*c-3j)AEzU02bE- zFi>%R^yP;h$=rM2u5vb;t2<6=KnN-kK`Ui0B#EgT19NHh%z!cc&wFC{zFr#Iv@vty z#JFnKXK(ZZJqEyCEtgnD`oiWdTOZrC`+*ImQkQGnR)z?HF+8m`Pc!pHA&8|UAp{92 zA%uXCf&>wW!C+#L%r8Is_3xXMMfW*ru?aw`Ed@$>>!CegecTxy{)8ZsRm&nGf=5IR zW7q|tiO2%5a*+j)1R#t73~Ml$W2K{>?rwXjz7U>0b28_L`fUKMt11BC&W{d%+)|1r z0cZdo5rt9;NJ%Lns3;|M+P3JlZQ12GvMZalIx`uoXgeY!QzAtY0)n9-gIh;-cv8}> z0P?FTV6F1(rd+N#4uF}NjDawQ(}2k^BP#^iM6ie;hzQ0IgkVDirP)Z$0tQ-Z!|Vmo za9fz=cMGec_zk%87kLa?+f+AIV)0K`fS2xdkqCD<5atJn{S ztUw5ou$TbCN|&H3H9c#UC#wL;5m6ohd_S4iT6G(i zLbKK^jbQ?V3}6WJ^D`-CxB!y2enSB7j2$~#RZ5f6npL7rtdiKkitCL=M;x1a9P0~V zq^5!}nMx8J2YxF`49~`i3KS(VTiUP$LjZwWU#AUE1BlxNIQ;k1$8*v$eUXaTQ<3(f zIA{gYRHNzDydb_9glfqTlUfj}S`e$`V!e^^qOk6B)QqGGv}H6vY#CE<-HU^X=3@Z7 zwg8y%v*&;Q%x&+TI-zXKE=5KeUq{MUaaic?_8QF#c{hrdy)bID!l>Z}v3q`YPWn1( zMKTGKlu=ADdEu0n=BL+9PMjKaz2v~7W@6S~|d zz)UrKJPsS%UO69z=BZ*L8Q)fmF!faS{Y`onT(EgvZWbQ4JjBf7%hoPIzRO4 z%YUdG9Q~EGuoV9gz}%X5!$Qt_{jYDm{_ynl?CkK(!^J|nWVaeiY2WvYvA?`AMgD-) z%Nr6+j*eT>7EX|(Y)%Go<)z>Kw(sdDzgc#D{XnG5iOa42ssqXT9_J<^{6?hmAMD-x zi~e*<48@u=DN7)o7RWdRB8D*xX=mJ)r>pz-?ModT-DkPJez@f&e+E#!=8>$w*Lg|^ z`r6Sy9Uwn;V30!;`VbVs5&^(5KA1d?`@iy2gpoc}tH<8~Fb$w~wb-H^fZ2ucQ7J`| za{>(ZS=juMt=QPF&|8>AhGvksXRt6o1tQ{FJ$`;g@>)BwMZ44M&Ye47&K5rm&yO&3 z_6;ml=difgz)~ZCmK9Vg{isyRO#tR{UGJIzMn^|?c#R34>YcTo`A%*~a5kNP9V*viL k(sJwgKU*3=_F4k`17^w7J@ISX{Qv*}07*qoM6N<$f`Gl4U;qFB literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/light.png b/lam/templates/3rdParty/pla/htdocs/images/default/light.png new file mode 100644 index 0000000000000000000000000000000000000000..88598999024753b36ef90492ff2c5e78dd963df1 GIT binary patch literal 733 zcmV<30wVp1P)LnxVyTa%M7lDe+V0@wuLmL4WM%%0)lyH|UAZ=8=r8YSE2FinnBDn-Y~AEu_J zUOoqq0pJu3ti()5M#g$ZN5{It;Si(rH(Lk+pT9<&K65FUh=1$MWD+v~3frdQP+#8= z?d!XKvAWtrs;VUF@i19Ym@LaomKbGJqPTVYev_goX8|Z>fd2l0{Y_0#iBjT`Boa{+ zw#(;}S_1*4+3%O@s0^ujPn&)C$cY00yxV~lvxr8UYaPd>j^p|nqaCtL0*o;#%S?B; zlan2)am#T+p>Pm@yb~be@gJIDID%4e%d)7~>&p{DyoO@(&kD4aPGi$0h?2prU~1&{(9X?0P#IOr<#@tP2Ga3=}4qk z)&OYu6WqTQux0Z;?rGJtke2(PGL1YHC3RagPg P00000NkvXXu0mjf$~;YF literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/locality.png b/lam/templates/3rdParty/pla/htdocs/images/default/locality.png new file mode 100644 index 0000000000000000000000000000000000000000..c80540b027830140c9638ac71fea175e604e4eb7 GIT binary patch literal 751 zcmVrK2$G&+uqi6(eYcU5)I!kZZ9DsC+J8oK-A$43>F0g0bi+4Pa7 z{=I=K=HT*zfq|#&*s+^|8C>38yc;kr$$OpmVuZ+RncIW8Yvn^B1L0-G7uUCBefb`-TS`kK>_gUS(QDTyJ`C{ zLdqP!pBTcC5lKW#jMko@s)!mU%G=RlybrWha`tXr$-=2Ct6n;h#)4Un207s4c~xTAaf3gq-2zzO2{2pB#0oTM=hd3zeodD?>?q?K_|cVOtnHE zJgnl13Wck1>e?g?o18Jkn0WoXimHJoG%D}LYM2;rB`_$;)!VTZx@p5o&fhVruVG?p zl8)}K8N>Tmb$(f~8jA^2;r-YP7IbtXBA5z(;%~NYT*Lj#|AQVo`;m`+KDo<~AozwS z%Q8|*NDx99u$YKRQSpQjFcHj*_PGVy`P)r9 zhw1|+h6gY~6JAIJwDH0C0vHk#eUKNE_5qABH1z?ArlN%y5~2->E>tL8?55joyGzS< z+u7MavvYmu&UUx0z)4PK=HA@%o%5aV-a85v_VD4u8dg~-6v~xem5O0m@~4_`0fllSvL9JV-j7CY4Iz_xp)NBJ}t716%=ET3X`H zojU;E1Kq1Z)&R`S&AkM0`t)h_>eZ`OFc@U}_U-K0v4gE!w-OGA^SgKNrmwHByk>TG zmIn_W01N^xmHn#XZF+iISzKJaI59E7$jAssjvR4q*s$S4uh%>3^ZDZOc)X*hr>8F( zjndTARFzn3Yb(H~tHiLj0syyrrAm*-bHAmfW%TILqZ8-OoxA*hYB_%VIKT`rU8%6D z3dYCB1%PRq@zWnHecZToJ^Z^VG4w6VDoCf~3I>NDI$EdAc$wk+7E?v6B;lqb70=I#G z9GVnogM~sNGZh~tKR=1N_=tSQAe&iW@o@?>lL0`}H1rxb!lk0QYuMG(ODq;6kx0kFNuoZh*a}tCQpeQIp!L}u@pZbB%J~@dHg1WjoR8@7t zYyz}(RRj_#B}j>sMPf54#wKPlGg$=CP+!ZYjd1o#oJb^s5Q0pJzQ)EzhKGmg>gu`- zybAmYpGh^ zZ)Rj{C+>@&z}eQ3utha6qH>iNI_LpRF?~(x@h!y5m^rzw~bv= zcV|}@hGC#63fs1AL)Z1?>J_E@?Ck7xQr=kI1KUCtG8A%)G#V|BJD@=QsvamvRYM3jE=?!6v4L7uVw&zM!8w6Kp%B0qfNXAVJ~cTx ziJ~av^ZEZd?20y$BC=S(DipACxl&IoWYGfPm!Y9D9RP>kXg_)4?VYEb9c@__LqkK( z^g`7=`|~#+Gx6^@*@YxoGfOU)C7;bwu!;y2YCJlFqb{ypyM|#HXqrYa7z7{|i=pc} zV`F3N-@hN=J>bVOz{`I9g};;esb@LO_U_%wz`y{uy>kAA!(pV9ba!_H^a4KtcPlGU z02RGkH`2mS!=0XgL<$v$b`1vCHwpw8jEbm`0k7Qlbjm-bPI SUk9fE0000T0}x3> zK~#90g_PS$BUu!NzpC1asjApY3MVE+8H^GGf*=ax6srE`v7b0fB)-XY;JD;QLR=#d!C1BnrN*_@a_J4 zXsu^#Fc@%mcXxh!d;6P|@#6CX)$q9Ag*;p69W#vBB!ubU=WN&W| zAq11jgj6cU!omWUWf4UY0Jd%8I1cG_no_BR)|ytUMY&v_sd*u#q}goZ`#xJ+Thr`^ zVK@zz$z)JUp|!>|&8gl(2y(d`qtS@d(^H+AG-J(N;Yy+@-F zQp)LF9LG}~lO$P|g=v~Jn@wu9+9Zmi5ugmmaeiN1T>RCx?X=d~{BSgqq$W(iA3jO* zd_16oAfVsxKPaXC1a1uAXW%Db9!ULgQ2aj>Fao{+UyP4{E-(iq6>j{y?+>9rOaen- Z^f!rg&agD@m!kjx002ovPDHLkV1liGn!x}7 literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/mail_account.png b/lam/templates/3rdParty/pla/htdocs/images/default/mail_account.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0dc922a0dc4496c8c26f3621ff5d936229beb7 GIT binary patch literal 993 zcmV<710MW|P)T5Edo{`z`ot*@ZbXt1}pSGJ19B8!WQtgNg&TwGkFSS*sy z=l>!4{^8zUDTPuBDJ4=$n$0HL+uH;|P_%Nn9Ajf+^tf8BqLgBMeEeTs_E<_uyWQsO z?2Jc`9#OB?aU4e&mSs^W6zK8T*cjzmB5aJ39!{|c@(RUk$A&(zFCJaMHM@P|G z3&S)`T-T+?Q&UrvN+s6T)~MBLeRo2LD~_d~MU1OuOC2wrzah$93KQTUc6a zMlxppi;vQLweSHc$3z1t&Bh-QE0t4*hK5*OU8URYk|YU22y(d`Gcz+xOiZx9zfYl1 zAeBlXr9^9urIh044KuiR%fUh;5`_?;mFCuCi0`Wu8U<^n5Ib_#{jsli|2Vb zjzghPKx@tJ?k>~Q(*RgfN{&MH%k#eyGLB^Jd56Fe+#R=R#)ACEpOa3fiQ|}bI*n2a ztu=;W^y_U|7TIi;R;$I<))sHxyuqqgs~mO%@23}m8n8oS?{$|U-(bXt-_LJw9EFJ0 zRb1CaDMgYbBuUb5yE@&X-25%qd~Uwrf3j~7jlXd36o+_~u^k`B)fTR6cW zY1aib-+uk&yV3g(VKka8no*mMlwhSGm0|YICnRPXg&=CRX*65^M-zaz1~tv5y!k_h P00000NkvXXu0mjf;yKpm literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/mail_alias.png b/lam/templates/3rdParty/pla/htdocs/images/default/mail_alias.png new file mode 100644 index 0000000000000000000000000000000000000000..4edbee4527385866b777d4b26bb1e10f196fa090 GIT binary patch literal 938 zcmV;b16BNqP)^c8&?#?e{bg5^^6Td9uW4(bYY|#x23B#i)KSqbycaWO4(GEsOl=IRH+Np z@(I#!fW(qr$p`47K`n}8L2H5pPGX_{F)=ha;1HNGW6XHwb&-w*RCJLmU7hz9@4R!* zyZ4ESi3y6uBDGrWhhnjK)3PiyO+zU~6zA8MLn(D3+wC@6TU)=%LZLvZRAPR9{${CE zqEIMc+xCA*&mWHVVHgsIA-?b9`#vWpC#|En@W z-}gB^J>}@=h_$sfT-PNYj|)voNiLTo!fZCn^71mS>mr1>kea6TQwY&l>o^YU>+3j< zLnf0!DJ3*r*D(x(2=npkv#e}`tXiRXEEo=3ag#&MiJ>Gt+EBO@aihCwo! z#57HGT}KDJ+h5)OPH1rN=X)&uvV?BvTzmf-1EVP(Jo=6DQkl2jd>bJIolXbWb*WaX zv|243$6JY@I9E2#V-r^Cw13I_)V2ta*u>thaD&n}SJ`B4yxy?TS+%Ppp-r;$<; z1OY)1^v65uZ(Y~XG>y&8P0HnRO8{R-WT{l@RxB2i?RMM#=*t_a!@VYt%8h5Q>E;#? zUjCTQ56{0BD9`h7UAKN!^>;t~^c@w8>pubmS93VS!P@cil)z4*}Q$iB}d>0za literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/monitorserver-big.png b/lam/templates/3rdParty/pla/htdocs/images/default/monitorserver-big.png new file mode 100644 index 0000000000000000000000000000000000000000..d40900d98abae5328e1c2700891e8d7e902097a4 GIT binary patch literal 1084 zcmV-C1jGA@P)WFU8GbZ8({Xk{QrNlj1+3MgYKATls8GayP~Yjt8ECu(VJ zZDC_4AX9W@X>Mh5Co}I@000AjNklFZBx>O9&-t_^ipF8m|jYaF(Ot?Kvbn*dD~bk zMX%kp_uYebRjpQSlN>rQush$*{O3P2-)soKoCXSgHZEU&pBIsP_rAF(^atPH-zS|; z1K>CgSFc{-#`OBZ@z-v-Ri~n#~qxPiGk$9VPR|5`Ub zPXz%0QmGWGss>nia{taPS{i&Gh1+-UcZ?1!FTdUWeqwThCx3{80aR6`*=*uCPS+t4 ziFD6jdj(!j*;JIU&u?vQz5ATWoa9(49t1E=lUOWRyrg z-~R-_vP>>qSnN=7)c{3{2A`m&@_twMzgz_~LJ$TrM|o>bzq40D4Q)+XGo{bL-{}KD)V! zLvXU{Ppbi&qIOt^dgGT$B?LjB(P-c}4yI|cxw%Q9@aRQX#T6gG!NGwqd($-0G>u3k z;$!4E4zXAaQ50ENSm;mGC%zKdw#~-I2BIi3Jv~h}n`LBVq|2yR4-XG%wOaqlBG7|! zxlA^jWqW&@+1XiItrnsv`j|P6Ln4tN6bd~*z|71HTU%SKtgH}^$4MrW3=a2w-h*GZ?-G@DHf z!@x96EX(STWBn6@s0ZK$k4B^Dy3Wzj5td~k%QCvIqpE5*ux;DD7dP~842}cT>vbxX z&JJlb8Yqf_q9|0WRb*MFR;!_D8ufY|S(ba_i~==48C0qSFavz*9zL&r;{MOaykY5; zkgEwG1;pKV!EL=4TLHZ&p-oKrThFZNQjs=G~@rjbXVZ$&C z_yZrU=a%Lgo7wZc0BFCok+Cysoruk>JTZj?uS%;p+*uOa^lv!1m<07L?4?q`!g3HZ z^0tTlp}b{!vuIS^qTGQg*NWC);jGMc-5>DPq+vBn`@3rzC~FVXzroQH2d}L#C;~|R z`RB=W0%1z;ooyW7`?z^%BX^Wj+rcOkixR`ePbs@;s4}T=UI<>9BInga8+luVs&yaE znFG@_kzP+j^f@f~Nch9oN)>F48RQf^1%l!ZV002ovPDHLkV1kMU$^`%b literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/n.png b/lam/templates/3rdParty/pla/htdocs/images/default/n.png new file mode 100644 index 0000000000000000000000000000000000000000..a035c343c36c323fe292cead8ab5acd3cddf2f23 GIT binary patch literal 408 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9S!3HE7rssMADYhhUcNd2LAh=-f^2tCE&H|6f zVg?3oK@euFPl=xh6l5>)^mS#w!on$PCbV|FS$#=GvOEa4?2#5M4 zCL|;@8tynZ=lA&o2M+u=d!%E5+KMPPAULMp<;CN4h>54iVEszV>c92klE;8b4*XD4 zWn8YPR4n#KJZfK8BM>}{O_5M$W@bJ-dkRa)dPmVe;xjilM93dxbd#8vEphFD+Hr$9 z8`ZYdh%z6)n*BeO>$F5&&40&zjyymRGku)_5c~;Wxk@=hBCL7e%a;>!fbFVdQ&MBb@0H1D`bpQYW literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/network.png b/lam/templates/3rdParty/pla/htdocs/images/default/network.png new file mode 100644 index 0000000000000000000000000000000000000000..008b03fb2953db89433c0835d7ae7d5eb1a1f67c GIT binary patch literal 1107 zcmV-Z1g!gsP)$S zK~xyios(TmTV))^pZ7g)d-~n-A+R+R!WiJjG>8LDVWVTTqSL6D$%REPoH4u5MH81L z3KiYdF`UsDP4of>8eK9+GLy*`-HNhJ5gE9y(Bb4m3basYOIuD4J;w{fj9wV@xqb3~ z^5psFNAN#%*!6lvXem@dDEa)@+wJoJ!2Op1*CCgFPyL>HyQA*pu3fttG+NE$yh6eK zGBh;J0KoN9ix~i0J0OIBQaba>8_h`ocb!$oS3Y!-pLov;b%YfYLilyR}N4?aI~*NMFcD&$$7Z zGopCE;|nrexA$8M3ek0`d%tgd>@Vc$HM+cLG-@Xxh^|CB`z{049R{wO~` z<3Zn?A1aZesCW~`Z%n^KnNeV~q1n9v5DBStKm2&+E=iUkW9d=~tE>NHEi6X#C%*m} zCvzDvG=qk`LTr&`jz>g$i!VW1Sq9MiZ_w&F9?6sfi710QCl@5k1Th$fQlfu>x*n;L z5^Uy7RHvID2?PeC37&8iVFnvnZ^%*Cn>kFwpZDJMmeLd;5e`&ZMN&**^qOb=jbe#J zrNg$Pw4(AJPzmsidU4L(gW9SZjE#<9-HqODe=q-Z_vxNgDpe%QGLm8v;AC(d2fyEc Z>JN%ZjYwq0u5|za002ovPDHLkV1g3I08{_~ literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/nogo.png b/lam/templates/3rdParty/pla/htdocs/images/default/nogo.png new file mode 100644 index 0000000000000000000000000000000000000000..cf1a2034f0be5f4d6ceeb0f6279b9a3a4fd413f1 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt^DJY5_^ zDsHU}cH}yuz%$3=zM_ukr@mhW|2HNG{_*&hQth1F=a& zK~#90os(N=TvZf?zkMzG_A{HzyDTD;kJO#WF2;@;AMXGrc z!6GR3MGJxwpHvWhC{+xk6eeJ-5=thWU}}=)(pWn=<~EZ#`{F|~Gl>B~f7o#LhV|{W z|MM@wt_B7MI@;UYzfnq6^B)uh!N|pn7heSa+I#MlK5W|_>gwvM9vvN}P$=vx7D9jk zfdB+T00>STf0V6!{tR#q_s#=a5Il_i?aJ@Qks!1dgw6rwS z+}uoabDW>X$I1Vri31&%Bt;dk_4Z&}7FW|#MB+7U3xSe~`7Mx2;kiX#$$vsuB8*%f z;nJ(;z8d`e2l21q&HOgkZ)PzDa%*|C5oo25QlXS0kO95=QzG|o!Li2)e#_GKz~bdM zpD`wEad+xIqu*x50kAfPa3}&P+bF3v$oyQE!Pa{Vvu*eMBQkmhR0HM89X#N^`tL z<5ml_5IneDf6!xN%+3-9VRoP;P0!4s;%^hGOK|Fhz|R$!Uer8!I89{rAvVUri%2$p zPVNVXLLttdega1?BQyj-!1ng`&V|5=OcEhVqEQe8;QzUaC}ud{y3Em34OMkEo@;o2 zG_en?tHM?E_q|@W1+hC4I}^cMAOC_1ENYx}X6M5Axg{QJPt#Ou(NdqM;Sh{XHu2Gw z>y=cDjg4K3L?V}4TU%4MZCBuXbovp1&oJ6>>*g$uY~WbUG7Yr?&jT!&U#SHuIX>Rm z+4;rUvu7Kk(I~cUBZMe}jj1@TwZ<63ndc2!YuKu3c^cbM+%^iLsBybUOQ~4UE|BO339-(i>Y5D#Hn2rP zVu4`C0d7xejjS*Kh@&E z+d$nxZjqZt8g8`VF>-6qAz8U@f0=RSlVEK8^G?9fL2?R zQjk6UjIFO-@<2%Z(D1mS=}k&pOMuaQK2g&%>&>h-B#*1`2jIHIKrgQK9BwZ%YMd4T_SVwsc#Z3u!0glO&30LFlZOiJ>@`&ZnU6Z<9HJ>Zi`!#iJmFP;Rf$1Rp6`a~Fn zXeo&FpGiuB)`rV3jFMQLhEt;i_ZB$0Eg5_3k})yK>iibt-z<MJXh>M8D&9&m4QpQSE`j_wdQ9iWmmb|0Bm%a-k& zHHmcRPi4DN=-T%zBATIQtKQP`g?G7KcBwhn=}qKW-&`T+{f>s5;B;1FCk0vvw(oRe z6)#@70nnYvKP#;`4&)j?dzn=0g7jiAsUJ zNr)#x_I}M{y?pic%K(XvZVnCu9zD!c-z=cRS+=*oOvV?==@Wz~7G9HY9k z&Y7VJx;rG<&I8gt@bhdpAAfdBSO@-L3nwH(2rS3N4+1jz0vC%z+@8KqdUXyr`8BnD z#q!i7R+#2gYJ+r#ARcd>$4a@Q^$(lPpmj2`1r!3LWmB48;{3=krTHbKWn+vc2sGJ| tvt+bpcB&64Y=#H-m|E!JgHKBW;1Au4U;D2T`V;^F002ovPDHLkV1jDC#4P{- literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/phone.png b/lam/templates/3rdParty/pla/htdocs/images/default/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..f319d1a62b9cd97892f9a19bc27ce5e739c86092 GIT binary patch literal 878 zcmV-!1CjiRP)J$}mwW&K0{cls zK~#90MUc&I97Pm`zpDP3nV!kac))*=42s4fN*34)oIn(TH@pTbHtg~W?0F6rY!R!x z01^rth-1kSLfFatxINvTo}TLJs_J6l7_M|T_vq@J^9gQ7Jo)^K;r@91;PmwL>qVB` z^}Z2t9AAHO|Ne`U)6q34<9~ye)Rb0n=ijQ9y#ZP5CUthU#*tk z#qIXbl~O;vd;9iJzyt>e2i=3i!*8EHd-i-|^x;rzEw;8c+1MB&Wgv`eTKnVi*jX1( zr;|UFQrE%p@$u%z_wIc^9`D~N`KonYQr4S;HqmZ4=5xs80?#?Dk zRWvr_v%^PJbV#~M7)4PB2tx0@5CQ@zi9|>%j;QMzr4$HHyWIlcqShLYSW#sL8)(AFv0Q4(Qjzpq;JH~K zEZ1w{nugw;K9$x)0n}b%oj}`$xH~{;gE5AxQa4Y|I$tactTF8F-o@4>8>0`1<2Gp= z5yV|4lL^z=3}Y<-(l;JmY2HmHESJ}WQJeL;=ISaVj60NN$#gnpSuAOq=Elk6g|0PO zo+A(x#fpBvPaq{#rKz++DTTH6|4st$z4usasg0&s6;xG45Ckl;j8+^|mL=Xb|B(or z#tUaHc+X%kU|p{1CLMwxK#u($hkJ2=yX*{Z`2*ZHYszg_c zvMjOII%7-&ATQIjDpsr4LO_xvbd!Wu9FcT7wBndvuZ#Dd*=#nsoX=Chg?YBf(#y-A zUcUVOqn(}YLA%`+08&cox~4XUlUJ|QY&QK>YkdaP;raRb3ivV4GZ#hCQ}2D>gM1$! z=bT<#U7gM6^Iw27z>4>p1p2@hkN~%L00$I63Y0+eFX~95*JqCT+yDRo07*qoM6N<$ Ef(E3R;s5{u literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/photo.png b/lam/templates/3rdParty/pla/htdocs/images/default/photo.png new file mode 100644 index 0000000000000000000000000000000000000000..f9574048bee0d664b73ee2cc800474bed183e759 GIT binary patch literal 572 zcmV-C0>k}@P)!DUf6tbscxxEkU^_yD|v%N?&K#CRt$@nO7ZLW16m zxP+KxTv%pydV0F5+JoQy%RYa6KiRJ~pH9lW0*nATL-L(S9Ga%Nzh5pE3#h7U@8!Ny z%6l&<4HTKh7^9TRvTSGP`DipkP??CFpMSWz`k}IDUFkz`b}fd^dq15{$Kx^lA4K$U z{}_q4wqG0_9g%S8`);*DojHq`sZl+)K#Cwm3B9fG#(P%9)(?B6F++AJQf9K(LrBn*OFAK+!lrS)pwN`6QL|SW3 zsjh2dH2^TPwKgdR4LRz{U{)4Sx;D)J+`KzGJ2`o?>XyEX8ImN8k_k!7v>JvXGF@JN zpU;;7W#1){fti&e)CTh0Hcc~|&7N&-`Yv2te45?-^q!MS8VqQKM8ruZdy`kkwpw!{ z^7Y#nYvJ(lKq)B8atMMc>D-m4r*DaJV-;Eqt3J{yh15RA#^24Z=a4DweK3ec;SfP_ zaHX}VHIj8YrNo@DZQFW&ho-S+ouwC{q34w15d89?@8e_Jw*LUW78*~2H{1CD0000< KMNUMnLSTZjEB@vH literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/plus.png b/lam/templates/3rdParty/pla/htdocs/images/default/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..f66b1a80934c34bf818efa50824d96bc138f8e04 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4^3?%3Nf7cA+1o(uwUbyq1uDc(|YY$R;1f(Q9 vT^vIyZY6W@mL@P9X5;u4)tJz%5Fp1e;Rpx2`?oLpKs5}Wu6{1-oD!M<eb9CPl4OP;RS3Qq*fTku|cbLIUzj z0s!*+6wGESMMtG$?TEcr695o$fnX4jrSaCK$84r%f+OSt?p22Xiy;0g|ItsuM#$VV zL8-ZPHb+vA_?W{vJ4d7Zvnw+H5ixR>rG@<)57B)Wv6#WBdnDr=9)LfGn7&OBJ=_87 zQvXN*02bW=vza2Jc!Wl|sBVW``;u34@&57zKN6_67t5_&dZeNO%w|d^31Trrwf&FC gqhScA?xm4`11O4oV#)QVVgLXD07*qoM6N<$f+^Co%K!iX literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/refresh-big.png b/lam/templates/3rdParty/pla/htdocs/images/default/refresh-big.png new file mode 100644 index 0000000000000000000000000000000000000000..e0021413b72bcc3786e1fa3c16be41281e6da806 GIT binary patch literal 463 zcmV;=0WkiFP)c z)6a{}Q5XgA&-pgq86_bpW9LUPOUlAn*(m>mEMz66lvv0gp~=EdrkL!kY$!V`OI9{) z#P|`ThS6g&_s#Xb^VHM#);;IkbDwj+s>)W45f*U+qsjdl6P08ZCmLxtf{iI$Qy-HK z;~aKk4zumMh8K8RRexZZ5c+_1Y->Ggy&o5FGs$R*SXJNg0IMCT??qgPA<4cZ2a_C1 zGE~(X?%`bnFXK(;;%t(I;39Ul``l@#YL+fx1)p#mXWE^g4cOkPt#`J6Rn<~eeXPw1 zKJRcxhc>ZMJMq}$*t>e!4eZE(FP+lKY}Vc+*IK6I+R?;E*a!~bO0ztNw|IriZGEe% zek7T}=|)^lRY`Ilry8*jeh%+(wW>b%zQ;K}#B0;RkFbFIN#>`BRrMQ7c!aMVX)H94 z<2~`ew+cxHJi#8D@vLCEs=oEOslUU&Jn!OhRsGxo<1fBBg}{C&i+un9002ovPDHLk FV1nwa&v*a; literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/refresh.png b/lam/templates/3rdParty/pla/htdocs/images/default/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..e0021413b72bcc3786e1fa3c16be41281e6da806 GIT binary patch literal 463 zcmV;=0WkiFP)c z)6a{}Q5XgA&-pgq86_bpW9LUPOUlAn*(m>mEMz66lvv0gp~=EdrkL!kY$!V`OI9{) z#P|`ThS6g&_s#Xb^VHM#);;IkbDwj+s>)W45f*U+qsjdl6P08ZCmLxtf{iI$Qy-HK z;~aKk4zumMh8K8RRexZZ5c+_1Y->Ggy&o5FGs$R*SXJNg0IMCT??qgPA<4cZ2a_C1 zGE~(X?%`bnFXK(;;%t(I;39Ul``l@#YL+fx1)p#mXWE^g4cOkPt#`J6Rn<~eeXPw1 zKJRcxhc>ZMJMq}$*t>e!4eZE(FP+lKY}Vc+*IK6I+R?;E*a!~bO0ztNw|IriZGEe% zek7T}=|)^lRY`Ilry8*jeh%+(wW>b%zQ;K}#B0;RkFbFIN#>`BRrMQ7c!aMVX)H94 z<2~`ew+cxHJi#8D@vLCEs=oEOslUU&Jn!OhRsGxo<1fBBg}{C&i+un9002ovPDHLk FV1nwa&v*a; literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/rename.png b/lam/templates/3rdParty/pla/htdocs/images/default/rename.png new file mode 100644 index 0000000000000000000000000000000000000000..320d2e5f174cf3af7c1b5559a86e483253a7f01e GIT binary patch literal 418 zcmV;T0bTxyP)F^cdONE`KCSRK6_m1ODkBGErh`4&LLa7 zM|s>@yS#!{tF@qbwOZwD`;F84^&H?nssm66p`db>WokB?ves%m9&0ojX*e8eFc_%c z?<-AH)$4TuGQaisGrn=Y0l?$&W8oJ51hm_204kNrf<}d>?7hc(Usx=c%lX`U&%@CJ z03iebN~O{lpxf<|BnigMHBFL)EXx)$O2Qa}wU$n&13miGn%i`6aDa%gzrVj0c@c98 zL$78*_yHm$;%Aw_Xp8CG8-ELO_9S(;Y1c3tD@Aq{)9xI?>7%HGq6lomCnj}ea z#wVa%x~O1xUAI(padAPX)1lYvl~wlpeU6Wh%S!9@dZl%z6ig-)bzN6I&r{Fy6wtw7 zFg1opqfxm&Jw4Sh4E6ExQKKkQK$9fVG)*jf1De@+x!)>@)@;hBtQT-fGhc7HK3l8z2bS10WmNEVwst&^33YXyH(^r XTu25@-HTn700000NkvXXu0mjfCl4p} literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/save.png b/lam/templates/3rdParty/pla/htdocs/images/default/save.png new file mode 100644 index 0000000000000000000000000000000000000000..41b3f436790fc9d996a410069fbd3c54e51279b8 GIT binary patch literal 838 zcmV-M1G)T(P)6{{8>=@8ADv%Z~mJ$)5fH z#KlKoK03a7@9qEM+79gQtT&gPo-|gSM&^Rs(+g_`&ex z;bVs1zkmG&@c{w|V!$_$KYuYi`}K>##zYlp`(K9F@4qrUd;JH@{s(l;?|=UozI^`5 zaPstZhEJb9F#x>-HV_~HhjWYrAP59e^xyxM6jX-An2oTbA+8jY-@VKR+y|kTQqRQ1 zn87{&D&gRA$BvFQLm{5rTKfQ>0D>3*4E4`IL;eFH9}hbND=Q-dGb01Tf1r!L|NF(j z`12 z@&hRPpJCzZJq*__U4a__5J1pCgv9aBAAcEMz5EGu5zuB}lzazi28x3W`14!<#qn7=*akf!_HB=70YV(F8N-`}gk*OpHuG{#Rf?ego=)!~#G7L1WJ0tjNjr!TA@UcO;?$Gsl>91cufEcLhKO@73*RL7=eg6d1#mEZM3lKnzpuphg_qfNz_+N;D z@izlEHwQS8{Qdh6Yzrt!gVewh&eyLWfvNB_kj45An3Q8=Y6yY%;cW+;rE@x_k9Ea`um@$oWMFH zNw~@J^eE-PI6^io(;CYf#=T+=Kf1fm7U3e&*Egv0620cr%Hpcur^&Jej^h9aaBK@( z8yjCXSJrOpTjoe_? zfZuxo9AIA$Xl0t;ucAEYM_3C$U<_J7g`$Y?`8=euDtsJ3hg$^>#BNbMGDLwv5(F^H zz!-%hN$@H%WJyA}G7Qr!w7CHoWkjNsBAd-)X?Ycrz{BfNAn**1!@v;&Ti{VFmC`Tah3_PPC07RaLB#MA-BbO^|xB^x)>yyT|0n4^vS~hF|mTiMl3W4V# z@jMd0XQAu4F;~E1GIjUs^!G(V0FEPY90$%WLpqhl#G4P;*3FC?fJQ1BGlfES)3VH# zP&nvS6a|cOpoAcqOyc>-OO#3!wLw~bqiLr4LbE41_+H8#SX|?JE)S&AJ2B)5AU@uX_;|ZpN<2Cr zx$Df%#WHa}@u5-|L@xp@NX9m4!tXI#AQf)Ufb*mPH8w$WMT)wSW6R tXmefnj=GLj1giJ>hs01_M`IzTyA?002ovPDHLkV1n&wZe#!e literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/server-settings.png b/lam/templates/3rdParty/pla/htdocs/images/default/server-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..4d8f1a1e6acb5cb1bd7138f30be7f5772188806a GIT binary patch literal 1149 zcmV-@1cLjCP)RP%G!BQHE)NWj~ zMXAsRDK6X6MK=|)h?}mYDcXe=L5kIwk{A^&n1PH*W+o=@&%LLM%nUJoNzH-7d+)w; zzwdtceD6L*a72M7U|0G1Prw0|*Qwuh3<1CO?B6Hbw>Jy$Bk%zb+bmFMo$;@K`2M^8 z_O|URo!(8dW=r?fy$5r^7%NI%IP8G2FEW`-|HB89a_g_#)YsM1*49cQ5kJ<^kv^78 zCaZ;7Yi4FFeu5L@>tSx-KIl zBk!F(dp7*&(W7^Qu&jWwpGHSVZ;27Ttue*Ie4g8P#z}X+LVbNbg+hTsp+K=%#Bm%B z9y~}Ym3n(KTl5gP^7)rv(cIj$w^S<8($Y#IkzjCekeZsU?AX!5p+ko_dh{q-Ym`#! z;Rh-JI0>BGxpNmMPJGDe)29iC!@SX(<>QY|U@6V;@HgDQf1kz0MT{|TM_+cr<1J|&;e)7aR^nKNf_T^A9lrssKx2-$3w zR4OIL7y-oheepa`+m|n2UKWvY-~sSEumwmwdGcgmXJ_ZPckkZC^E^tW5?x(gLX<5$8l(CYQk}x zN|T-P!k+8+hBk$j6LV>4iRI;GM1)u@MkEqhnQU2WE&3ljZAef#HH|TZLLq$Luaau5 z@qHhybwxqoffPibSosk}Dg+`@-4@q%tBNY`$`L8gdyXiGAOs|UOy$rEqF$YW~utOSM{ST-PNMiBv%y#~~C7k z2HpiaRw-4h5>}e3fvdo;z%#)AKLW~tq4KKiRds8x!ivRRz$@QB=cIoDmJ8@U{kAEW P00000NkvXXu0mjf?X@7& literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/server-small.png b/lam/templates/3rdParty/pla/htdocs/images/default/server-small.png new file mode 100644 index 0000000000000000000000000000000000000000..3d1e0f6f2d001067cbcea1886ebdbc288166253b GIT binary patch literal 818 zcmV-21I_%2P)4Q_ zK~#90wU#|g96=bzf46TJJzv-h1Y;XWagBwQkeEmy`Vp-C0M<6aGTk*6rm^)igg`3G zkm|rngMx$@WIb1J?{;@)pA^n6yXSerK?a7~nc4gQ{?9Yd3=99c)t*?lflp)BkAUx! z!tH~DgCniAD5WGx5{ctj`u)B{Q6#-yPxkls1^76T`p(YIky1*OQj(@=0T>Jh6-c|? z7U08UI?iJYmSw#;J3GU5-C{1wG5|z`EX&YZv%0zp@RmnX^(X+q^E|GvuM5E}%Mg)b z97Pdxb93V)#sktcErf@~%g~7cBErqh4O;6$c(^;xSbt9gn$0Fjl2qK6dmP84X*wAI zKq*BK1Y`HW_x;Huh@yyIuUE-?`8kRr=I7@p2l&2^@B4*Y+qNqpfUB#k$p8SIPKS$& z3w+iX#5b7uYVc} z))ubok|ar`n#%Pbh9R4qn*d)H78Wjn-@qyGt5&Q1agL9VyTC1Q!u{>(0M7dQ`fi^4 zQQ}%_9LGUK2!fyud=7#@0UAgF1!xDbjIal|z%yVDKx>WGx^kk+XJC4II*-HyW{hxR zGME9GlatJXZC;u!yG@ehL5U_wLcLzk0XEPFZj7E7fH-DQ-aQ9slp|0|Jva+-92c8x zqMk9S&P*@F@>VlWT)-*Ux^3G4m21MbZ9LB-j^iBA1VWR6!t^k}GS5+`(>dMS+k2Hm zM%_gK=jZ1Dzs&zVV^Wnx^JW?MQ$xLO=3VnnIi+G8_klYT!JXMp83o8Is#Y53bC#JO w-m{GRBFT*Nyl5UokdH@wX$;TFm~mz8AC;p_HNj{qe*gdg07*qoM6N<$f&eym5C8xG literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/server.png b/lam/templates/3rdParty/pla/htdocs/images/default/server.png new file mode 100644 index 0000000000000000000000000000000000000000..4f6c2a4db51da67c2ceab8c2c37fdc5d87658d0b GIT binary patch literal 1424 zcmV;B1#kL^P)(yph*Sa;x_0SXT zUqJ1>y;LoiN+5wyMb(N>mVhB7b<^0f<7M`(&o|VTgP{_i^y*t?=6SyNd6(hn^#_kn zA}~=91X2ox0!+f%?e2B9Yme4Dqmbh0=6}{nJbM?$iJ~YKOQo5`6DO2RCd2cBu#m}S zQ@W;Gf3Ds68U$BHab8JqVr6BuP%K`~DTRtpKBXtJUN&DKpCit(U9R8P$aWmEU6-V2 zoF>a<`u(@t9|Qbbm^UDKB|))RoR3fB4_Vjhk^_6bcS#flG7Xa_ zj^iV;d>UA(0p3d`$|=fQx#DDI&vHpHEz%8>4Bf;n9kLu7H-|wY&-Fm)ksOw&+w1w3 zX>x)jDAyO1Kt7^Ecw7)axTKoodlmuAEcdyccwF-9;mJ3FJ~3sa_;A`Lh0 zsYW0TaEP~vECk$#fs;JUsYH~ba)f%lULd2cst-Z1J0KY&NFI%1vD2Wftu1Qo>`=4W90pUAB$8#>mm?8x>&X*a@Aduw<`Gy67I-m60Kk*F zY0lXQmQpNIKA$HvHCkR;Btev@)9q5Hvloyd_o=zl;I`{^A(2Q3s-~{0eKq3S_NO4& zf{lB?s#eM6ptV5`3Mvu_Q+8tFxfIZu?L{$5$z(FfnvIPOq^uL9Y+u)@-&Yy#aEyAu zADf@gCsQe@TrT^$T#jSe;`^*x{UH4u7u^%2EFGJTCaphwK#w1;!!r@Z77l`pBJqdl zSPTgc9zMMI_Nh~!LH|ijRsC%MNs`2|mJpR?f^|$pIp3nd46F*VYg#f==J-7>4qKzSjCK97I+Yi#b|zMiF%z5;?W7@ zyMO;)A5#5C>l~L^7e2N+pWGWmZVcm{d%LcB>soRYLHg zAI^lJ_ps;d4<7sg!Y`1jfBlCYOlVd62i%=|b9VN^d#kJGs@3YzLaC(Sjqp~z9%x^! zRH%VeZMAl3x7DKB;~K54t-0%W?|$yu_BZ>{Mq)5{fZN=?fA8KS%-`WRc=SN|z}Z+V z{=wnu^wH`YM}ps^fZ}2;yn#SJ+uZc)o14F)FMU727@fyF7=8G{dhXc5!iCdk&YUez zP0gVdDK~E3q+hOHy<+M5mmt`9;r(FyIt1*4aAkU0DNRn!MPjitwOVcBS*`XJIxGX< e-$ny?qJIJ6eZ8O%c*~gp0000UP)4pXFCHvZDpV>$L9qlRL?K1g)MO(yP2)ywlQecW|6H^0ZFb-8 zo9UseX#L+?2ZmvIGr#X|zB9k?_rd?YiDz8b1gr$!20j2BAP2k(c+U$eP19W4-rl~u zv9a-}ZQB{sH1m7+?)7_mdIHyV1JCpPiHV76$8pX+_mM)O(81>B=HkA6`-1c5&#Pjw zSW!v^T5Iijp6=`G)9G|t&&hG7^;DM_c( z2qCCcDtMk3D5aiCaVVKgZl9Z*JEfGW357x-fMhaxZ$W*1J?-u796EG}SS-f;{5-i_ z4k@Ju@=v|wEnBuM9yxNv)>+adLYZs?apAPQsHv zZLFGF28V{4J=a~me*JpGaU3x+GSXU8Q}bnOYb)2TUDNjfGMP-pb=}^9fq|*%>FIY8 ziGPLf!9+%C*g9k~c)A)Xw3uk)RSQqE?$PhpbSg6($ z_W>vau%(n|PMkOqcO0kAG)<(G4+K_9aqir?#jdWd8{^~SGaVfru|jrMhBQpf&0rcb zU(q@RgsZGPP=La=?H9x0@K7`wz1h^%l>PqLG51O;rElN9t&7E?zI5qQ(9qD30d`3# zx3smjy}RelZ9O~JC-kQJl{zeqf`GSxRX`)K45$Unf0w!i^hFFa*|DZA5?{H3O|NZM zLjwbgH+~*GDq;9`#rJbTx%A=A-TQw{T}f1wVrX^>L&)!Z6?_9^t8^8Cf_Q{}55Cd7 zZi6j^sPoI7p;VnMq;9h8Akf+?DRpO2E6?gothsvSm#AAVQC0z0=d)T0@rC+_s^5V* zARl^2s}YMEHmn=mzWMdd;X<)~axf(qGFgI3B_x%yOheX1q(I9MrJ#b+no6K(jV@DD zh5XCD4i@e!WokM-=+cs)ZH-ze-OY zD2eCrZdnDZ0qTHAHJ}Fs)$ghbP^ykf{Kp@K`zz6?&Se$oZr1>R126Y5vM0>T^Z)<= M07*qoM6N<$f{EdFd;kCd literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/terminal.png b/lam/templates/3rdParty/pla/htdocs/images/default/terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..8f072967cab9c40bde1c7a89ff4602266c15c6b1 GIT binary patch literal 851 zcmV-Z1FZasP)32-t&mbu=9tKQfP!V#iL=o&XO^5_qYdH)9K6ru; z1o4EZG_GZRv}Uzf5^pyFNGZ|u2PQvJbHu7DF(z_~Y`1?ijw7WMOxv)!v%)${R3&Oe zM4&N<22A6q7?KJ;cx(uPAVN%$>+5S)s};^UR?7u#+tPI%(K)0Qj4`B?aGj%R8XD7} zs;KA;q$&g-7{?J~4BcWeTcByEwPLL$+f36m7-OJ9tze8nL{Js1A1A!`q@4Nst8aL9 z^NP)8gQ`$Vq1M?~r4(X}#2CprQ%a`Pj7BAh&`iUC_nunemnTnHE>}Ey_=xq8m?1UnoCLJ&li^Ye3dyB$A2e$36yOU};DI6gjRzu$9t zc}d^*jN`~OO{gj*XHtr&Ds~tLBm_i+i;D|VN?2<#rsBQFImbATY`0rtj5CM`UJ@}S zQq05{v11V4d3VkIdmn+B#Vw^^N+FLEnhQB+j$B7+9qVpM$_mv`KX{L>b6^a17zRXy z_uqc&R@50N)|p9F$vKnroVAjnR7cl2j4|x{E!2uV91a|Y0V;r@mO?J&zu}xSDQC*- zz7%Rns79u7!Z}Nf8S~~Fcli9XPdPdLusY{hE>|qNj?OLSY{mJ7L{&s}HlY50wX3V= d=Gn6={so}?UiTi4LoWaT002ovPDHLkV1nwJbo~GT literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tools-no.png b/lam/templates/3rdParty/pla/htdocs/images/default/tools-no.png new file mode 100644 index 0000000000000000000000000000000000000000..96a04d1cab952df906da9a7c824290c60524b51f GIT binary patch literal 798 zcmV+(1L6FMP))%Q5fn%!4!42+UDGb5N)zE?_QiOr03lY&vkw8=i<2%|2x6G zn2u@4#Xl&1EJFn@VhEE_jzx#qk|Of6p#P|of;D@H z&h=~KS>k0@1w}!AP!>Ga#?EHq_mwOEo=ybGn88y)I(YFuqPP3cNC=F-4w6hHI-8o} zGop>1L9SNn(^$QwjL3=R&) zxihE63?3R?bJKF-@WhG7g3p89!JeQo8Q}E;>|C<@FU!qjh9i?1_VZ6ilDF12 zSW#HmR?Xt-{{Ex$Ie`JRqZ$Q*r-#^EkR_fUB}d1O_v7n|ifAF)^7GFJn}hnGFqje;wzwtx6o^wNPsWb)_COSq z%CI`v8@xRZJ&iPOs9h*EuSV011#XyBs9@$?mrtL7+h$i*{&BhYlJ1@!-+uT}S07tT z=)%#Qgx~5nZI+r}>-hN|Wg{bwA3mrfD~8S>r?j}lOZV5=y6|q_@a{z}pe+X~)<5rH zCQp9B1T&j=srvE*UD=eUL^hwg0UBB!Ut!UVJB(CSnMZlTP%h?_%(`dx>EUboE}V7t z*F)Z9e-XE1>1Zn#jAvRMU#)9ze_+q0O9uEM_%XpVSdJ%P7fWBuw(nL9kA9pYbf7cg c`1SSw7dy%o^hCoiNdN!<07*qoM6N<$f=ecQasU7T literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tools.png b/lam/templates/3rdParty/pla/htdocs/images/default/tools.png new file mode 100644 index 0000000000000000000000000000000000000000..b391a0e8392aef528db3450a6ad70708d75d5d3b GIT binary patch literal 503 zcmV1`c4jv^a zu)KIs0uh982M-p41cI_d5ez+97Y~(+G@b3&N~ib-oK89ga~VEee(#XE7Dn&!{eIr( zectZ_|GBOJ07{$Zcb85i)w3YEs6X~r2D|T+by^3oD0C2zYQ6za5yYL zvmZa`;e!tAU#vRZ**C1DlU2`9T!0#z0|0W;X+0b0H<5-)PSiU%s-%-ucWhiwM<&(S z9MJKY_w##t()q|lyf%0iP;6v^n>VgAG4-B0? z{vz+5^Bq7*C#&9}xcXmC>u!6OZK*AD>9KU8(WrnPbMX4j+xdIDxg0y67ML81;okY9 tOrQwVTnz;6w9-G^2VNaZpy@PO{{}M@%mpE#!`A=+002ovPDHLkV1kCObKbiiJTZ1$>?U{@q^uA>U5jA;pP+vO1oGh%ofuhDQYB<23k$0 zrfEMaSc2?_mmr5xQf4K?)M1skfF}S?0XhM?0PFzW06hTD z0R{k^1mWazrvbhIdA;0M4rf>;2U-?0Qh)3AD>SG><~ndvO*zA5I&J8oRt+O2vQ{SOC&*wBrK6^%48@v7fGdL zL4jYb4yx6DjV7$sqLLD_tSq3@p^6H>!Ei*uXgoF=k->0SSsA88Rn@N9yk|BaS}df+ z^4DTPRx7HnN6pQ9Hrt`i_RnTRHXCWT`|b9i-Hv*CP+uQ<@gm@G1RV}EI*P`|j$E#w z%Y|lUklT&s=h5OKT3SMDYvHXew7rdXcF^uF+TRb8B%%-qpkNRkAEQtRA+&LK@mb{8 zbsAMMCRzJ=D`Je-Wm2q-eojuA_&ai4Eru}@Bcx&d14&dETc=Rw#V#|F@x)BN9?I4)Bra$&PnyX-z290!(WyQ&Z#dbo1EK*@3~qrYs!S zq;W-%GD9Ev=?|^Ba;2 zS*(?Z9K*Vs!*Q=0I1Ev0>r7eMvM&1m;5a|y(~$MVZM{At=AgZ$wYRs|;UcD7*h#YkUjF)zq_I>QGFPr;<8z^ez7Ztgk)U literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse.png new file mode 100644 index 0000000000000000000000000000000000000000..1c9be7591474dec5e8badf0d285f8f38007dd5fc GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!VDxAh&o3CDgFST5ZC|z|IeO1yQQV2uC5L! z_ISgNdq9e@B*-tA!Qt7BG!Q4r+uensgH_f8$l)yTh%9Dc;1&X5#!GkW{s0BpOFVsD z*)KD(2{8%X{ISLpD5U4<;uumfCpm$EiIJ_OA=oAI=7|#wM;HXTcmkMwj~qREaDw1| isZ<36g#>XH28NnYmWOM7ZafF-WbkzLb6Mw<&;$TqC^qE) literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse_corner.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse_corner.png new file mode 100644 index 0000000000000000000000000000000000000000..b3328adcc2ab423fed5cc45a7105588b19028efe GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!VDxAh&o3CDgFST5ZC|z|IeO1yQQV2uC5L! z_ISgNdq9e@B*-tA!Qt7BG!Q4r+uensgH_f8$l)yTh%9Dc;1&X5#!GkW{s0BpOFVsD z*)KD(2{CDAe0I$R3h8;eIEGZrNlsv3Vq`062zH6QdEx}a5e7jno&YA_BS()OoFKSg iDpkSYzy=m+28PP1EC&ljIDP?jGI+ZBxvX2CMB#UP^@JYD@<);T3K0RR*AE~fwh literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse_first.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_collapse_first.png new file mode 100644 index 0000000000000000000000000000000000000000..5c4387616c2c3514ea9edccef686a72d427ccf2a GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!VDxAh&o3CDgFST5ZC|z|IeO1yQQV2uC5L! z_ISgNdq9e@B*-tA!Qt7BG!Q4r+uensgH_f8$l)yTh%9Dc;1&d7#`=`_i9kX25>H=O z_RFkHLK;RJ_H(}j3h8*dIEGZrNlsAU)zFrflx&btaB5`qTF|*;hlPaEqwvK%ENlYG ftOh!cf{YAFt}Gkl=B@Vz>SOS9^>bP0l+XkK0535l literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_corner.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_corner.png new file mode 100644 index 0000000000000000000000000000000000000000..541c5b3bafea8a3e2599b173cd5b56d7887ca711 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp$P6SizMjzpQfvV}A+G=b|DQd3cHo=2ML-e8 zk|4ie28U-i(myGkCiCxvXF#WAE}PI3YR6C+zmL$FKa%@ZdWjxY#v@gy8-WYl`ZAX->h qXprI}=(wWsRS+8!GgFx(1H*|>)`yP{9O?&}z~JfX=d#Wzp$Py%F#WAE}PI3YR6C+zmL$FKa%@ZdWjxY#v@gy8-WYl`ZAX->h qXprI}=(wWsRS+8!GmrNMMuwwPSr4X~T-E`az~JfX=d#Wzp$PyIl{lLK literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_expand_corner_first.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_expand_corner_first.png new file mode 100644 index 0000000000000000000000000000000000000000..5de11a6ba812abd5d1a8b43033263a0567f18899 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!VDxAh&o3CDgFST5LZhEhL)C=y1F``!0g$x z+xe!10$GeDL4Lsu4$p3+fjCLt?k)@+tg;?J4rhT!WHAE+w-5+3UbH=O z_RFkHLRws_x-WhL3YmDiIEGZrNlsAU)zFrflx&btaB5`qu`qCOm06&qr^naUqoKrM n8W4NRA%Vd#h~co1h#3p^r=85p>QK$!8; z-MT+OLG}_)Usv|aOl(3T92QIuo&be{JY5_^D&{07I575b1+*DVIhY}&!68^+XqKAb z;jL{_Sjd@>@OqBkn@5jM9hhJ!%(9GGa;icATZ#{RVw&)78&qol`;+0GE79%K!iX literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_folder_open.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_folder_open.png new file mode 100644 index 0000000000000000000000000000000000000000..4190b969845fba00d0ac6ca0c5a889818eebbeea GIT binary patch literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!VDxAh&o3CDa8Pv5ZC`8a0bXTo;j1Dp+QMW zDJ|^`P-y1N{|62nICJLzF{Y2Ffzpg6L4Lsu4$p3+fjCLt?k)@+tg;?J4rhT!WHAE+ zw-5+3Ub*oF%FT1tLu>VJeJQnH(d+g`BG_ z1$>ygHk39sh)pnLW@(xvAz|dGwAPD9;A*CWpqi>dgEc2Zp{e4eYkN}4fi^REy85}S Ib4q9e0B@C3i2wiq literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_leaf.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_leaf.png new file mode 100644 index 0000000000000000000000000000000000000000..6f999fba427b8c3f54b5bb6fea272b44eb821bb3 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!VDxAh&o3CDgFST5ZC|z|1$tlLjzEtp`pPo z#jzL2Vk`;r3ubV5b|VeMN%D4gVd!9$^#F1>3p^r=85p>QK$!8;-MT+OLG}_)Usv|a zOl(4Oa%Pr$?SVp;o-U3d6?2jk99WqGW(9Jd5(o-v;L^}^WSuf4k%=vZOG4UOb2kHH qz=@MA9@Y$!=MuP17!*1d3Nj=-;}V*1`l>L{5C%_IKbLh*2~7aI8#Iyt literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_space.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_space.png new file mode 100644 index 0000000000000000000000000000000000000000..fbdfa6a8e1e456ea42a1d39969b15e295a06cc75 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp$P6SizMjzpQp^E9A+G=b|6hJY`UH^6SQ6wH z%;50sMjD8d?NMQuI!hYn1v)PS~ujE l0fl%xT^vIy<|HR5umgE!42;`KcIpCI44$rjF6*2UngHK-BHI7} literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/tree_split.png b/lam/templates/3rdParty/pla/htdocs/images/default/tree_split.png new file mode 100644 index 0000000000000000000000000000000000000000..1a49fab4550598ae110fe90c7fb77c4b098a911f GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp$P6SizMjzpQfvV}A+G=b|DQd3cHo=2ML-e8 zk|4ie28U-i(mcdx@v7EBj?09tKvO8Q=NrfI<^XTq8=H^K)}k^GX;(^7C_&5{q<; zGpkZl6f%?Z^SEr002h}=vZjKOfq}7tS7LT)l7f0xW^Qg`vbsWWNor@#vd1gt5LP^Gzxl){mM<(URAA$D%Z=e61 z=T6uDdeDkbR`SWizv+AC)j7xK+aLVZ=45(OZ=J_k;hWz4`Yl}>CZ79HSJwA!o1T-e zt_4e}p8oO~A?ykf^Va&8%_3Y=2#I(As@WqW? z^Ov>eP3~A*^!OIb=HmR7Yk7ZpK8WqxvB~S6oWdRfHL3p4lDzDz{k?PF|6ncXcfT!=~FWKb(Kd&s%=E+VWGM9x&4|c)I$ztaD0e0sx=`fSUjS literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/images/default/up.png b/lam/templates/3rdParty/pla/htdocs/images/default/up.png new file mode 100644 index 0000000000000000000000000000000000000000..865ec1551b0b7e030fe25b5ddd76dbb4584f33e3 GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp@K+MI#1|;*}T{!}z*pj^6T^Rm@;DWu&Cj&(|3p^r= z85p>QK$!8;-MT+OLG}_)Usv|a?4k@xdf)zj0m?ISd%8G=RLrTJXz11KDA2n9E{llr zqPYv2S`!4dxi@awBFMj>^#SLR%@qvVABDC}%UbHOfhTF9!bO$e|2OYGpR`y}tn$OD z{Cj6?+$YYMlH4VuP@V6*n01}!^3R^t&oi}x7xe39=sftt;}tJZeS&R;oRTp|o0H4o zGk4++F;7r_63`G+{M~@z>87nm{w8a&KM{J$IECqm+!49(>-lfql`Fb>a0Wg5sk%6Z z?T=S!dF0pEtMv4znN*#ce?5H8IX^Me9l5LOAG5BAT*0-cu;zv0HVfPL!KKdv=LYx) z>^rI0Jaq}v%&GG&j$dp{FU}Ra$+4oPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOb@ z2rw9brkO?n00?zSL_t(o!{wLTZyZ-0$3JIgc4v3JYddS#mnOk!Z6~hNG-{d)Y7`n# zikb)1Qt?oVM5MgH1OEY1UU)&MFBG+@RHPstnnJ4}l>|xwQ<{V(z1g`mjuR)ot>azq zW!5{pp6i*zLu?brE{fm@@soa~j~d_>)$hywk`iXi6oGxRmO}rj+sVOx~GTmw+ZXoJwfNwsv$@4(%A|=zp*?mQ5z` z>NRZ3LZJYGkRhlDO$c-$5CQ}OT@z^H&LiMc3_YPRAaZ$vYDw(`57*hVf#n znfUQfzx`}VI1Rm6DzPXuQtcglW!G0Rx;C?U`(tFYS<>k=u~_U5s8T4UR>sSwl%i40 zV~u@5{*$xVvs2WI1?pbF`Pm|Wx|Or%7u}md$m7C{iKoBOyYc-e`uoC1TT=0xxAUaB zH?d*Ye)c`{EZtik!Y~ZPlC)72tpd6i_}(??d(?AN%zXGh(;t1nRI$RXs?CYf@p|1+ z8N)EN0|)va7Ck8=K7Kn-_Mv_@e(OcP{>(R5KDG?1X&Q+{Vr4FhBBYcALGY!7gb^dN zrH@B?dWmJ4sh>Q-WVKFHJSOXoj~2>kzYyRAl62glwQqp!kL^S$wF0s{HVlJuxy-R+ z$9VF|C+Y9+XK-+kUAuNMG&F?cI4ke0=PiAM#0?Fp6uK^A0Gck?r3o!kD0HFG)jxn~ znrmLxGz}pHZ@>LEufP5}VHmP|_ihFT21uvV96frJ3l}cjkzGoplq(7?k2S-<&@>E9 z(3Ffx0L?Ho&(JkM;D!=avsNO$SA`%5ICbh2mSu6|$PwOn;|-1w6|!pzQ-+kU4C4lpNJYeT{n}DpiIS;nqA8UmVQAC}x2etN*F+Em0a8j{ zdg&#$ZQDjRo5e5;a=9EMBO?eQ=!pp3?~4Lcuf0)@|-=@ zpgG@^NOaZb3*;^j)BSKCLWnhezHi??dU|@;x^*j_=P@)i#HXKrN=r)%2M-=ZO1UO_ zUDv7Srl{v8nX5b8tk?3Ml#>9O>qkS^s|$=3D>vFRX)N1jabgV1verZsMG=Waf`Nen zqA23hrAwSRaf12zc@7^w%u`Q2wQl}}5R@){j2DL3UO?V;^dM9x05rQ1e)#UqiC_Qr z((uD!JW);>I@Pf&-2CD)jYeZtQQck9g+hT-r%o|FJ|F=Y0CENQa2Uy!f+ufh%otY9#=kbg+}zxk_H?CEVPs^4cs$O| zojcKWeck*cC8f{)f$jQCRBgr^HQf#6HDKck0RY#NM_xO7Cfm~7Ov2EqT)jl@@-VLJ zu2G?0uhZS#&7M7b&@>IhFjlFvybr*eouu&jN91Y^f+z~=L6EQ7!8?EnX!%@& zFV&fMZ%h`N6DCqc*q@&xH-3FhRZmS#@zz^!ar*RW-hA`TRqKXf;LqP??(gqWY%Fr4 zw7`^8uU8iQcU(Wp1KyIO-Fqan8hrQMt?{mch+91yn#qrkG5y}}$W2cZ1OZVLQLooA zO_RaFK~kv{?d|RVirg^3X)y85amuqZTrF1Boa@x)Z0C9)Reni&cbR~GF9FZ! zR-S)u=jKhc$FxMHT*CK#jJ8gi(&-hj{{DUr95}$ig9kB9lSCqcE<@&qPLm(`gmJ6J z4a*L$)vA?pE%=KcM3;d)Ft_ANU+<*q2T@K*H91-?erl7?710eZO2S! zR{jHRZEd7dDN@NKeksq)$zL)5*?F!OEygRh{LCsXP8FQjWTd8n954?Q0cTwWmdy#| zwNT3Zcx_?t-i;47rFHbcDpQ}EA@DqsT^q?{GQ?srbfuX8=x>zH{+a5{YfM=-qvZv8 zrD~PO^Np8*X<%~cSq4h~_Fizeb!8JTXH$t6ez;>>M@K5rlie-P934k1(v zphBhMkx~UWh<+J_(Wq4mt}a#ke|9&#U)#_e>s267N~_x8)V=xrpRj)dzR;x3^Ym;Y+_7ps%hyT5us_zQ>b<=1uc@=5TOeO8Uqypfo&up zLaXJap#*tsEo^x#ujR21c9-2{*~je8?9cvh()3poen0nrx9O=76kkk4_UQ-|7aT!< z&vp1kkx4I}_i(MsQsiYFf;=q>)rw@84KrY!lgYS1ypi}MH5^}m9tmYxG}^0TFt1iJ z$!KmUM#jlKxR517)#d$Y`6dRP4M*W|S(Gd~+-sE&h0D<`{8QX7EJs$jyG6rRAf zqSv7M;VpPw4TM>S=q`ug9A(0LJWj5@;(heqjD<~`Mcf_m^Of*>wM=-msuK;0Sd4ca zfzMe%_%`@GwS*Oz;z#n4&&zZ3>Uy$yCLD9w#J|kM9q_N!q`}NUHms8$L9pK;9em8( z0U1&+c-J|@=s7s_WP@F-OV_FjdQ}>{ZvC!s&*BgTo->UX!K#&P7<>&nj9oB1%7MV! z;-4iiq%l$8EHjm`XiCYJJNwbT_#IZ%`4D~Xkn9~yx2N)jZStS$SMpr_g2`ujzumsO z^$!b2ux|qK#XF`cKb_rmdx0|n zV2&3r<%X-psmMs$gS@o8P~=CVUXcKsnXSf%RL5@J{EG15IQv03E}x4)UD+`>Y&xd3 zsv5k9Oya(jBSTYJ42D_{Lw)x!rUyP{!p~|{NT>Q#kRd}&aTIzR4`HY!9us|R_#fM= zk)JL@qaqr!LrL(iH&AZB0q25*51t>ozORA&fU$Zv6R7QE5q7sCKGOZDwMVWhe;4AF6e2`|OwyrvI*&2Ph? zItHKR4E!!>Z?JP-+8)ni!!78pkH^aB3F0jv-Ko9_>&!7I$rA-H=U$tGek2vH)e&ah zH+2y?lcpR-^#ynx6NC-!x9xKX{P%fGzw1>W!eQ)W(m|wJXr3&FdFo2Ybuqvl=moEX zGyIYZ`{MKWXrj->8OP*Q%OqciKIZ`M)_yY&7s4_l?K%D%3qIbOqF1F;u54$r(^lE+ z&mw1;u7GL05RyIt9Pe6&j^e~=?tg*Y|DGEpKS=(N{Nh=gf^H}o9=rOba0lir#iMp0 z8B2qwz&jUSDz|oa4433bH}drPG#S2CB*JcG%kKgGL+z#dRFIQ^oRn~sFm^5_XbwRGvLzY!%Vr($FF|LDks$M)*ZI^!rTM2 zH;9H-l}u|<%8d&U*S?@M^A9U5z9k%7J#VgRE34fv$f8it^KdHf!YWgs7<73dv71Ywb6(kAyjmH1L aeLN=6lj}9Wdjx2srlVH-PYc`fti#_mH^Jio literal 0 HcmV?d00001 diff --git a/lam/templates/3rdParty/pla/htdocs/import.php b/lam/templates/3rdParty/pla/htdocs/import.php new file mode 100644 index 00000000..22d7ee2d --- /dev/null +++ b/lam/templates/3rdParty/pla/htdocs/import.php @@ -0,0 +1,111 @@ +getIndex(),'LDIF'); +$request['import'] = $request['importer']->getTemplate(); + +$request['continuous_mode'] = get_request('continuous_mode') ? true : false; + +$type = $request['import']->getType(); + +# Set our timelimit in case we have a lot of importing to do +@set_time_limit(0); + +# String associated to the operation on the ldap server +$actionString = array( + 'add' => _('Adding'), + 'delete' => _('Deleting'), + 'modrdn' => _('Renaming'), + 'moddn' => _('Renaming'), + 'modify' => _('Modifying') + ); + +# String associated with error +$actionErrorMsg = array( + 'add' => _('Could not add object'), + 'delete' => _('Could not delete object'), + 'modrdn' => _('Could not rename object'), + 'moddn' => _('Could not rename object'), + 'modify' => _('Could not modify object') + ); + +$request['page'] = new PageRender($app['server']->getIndex(),get_request('template','REQUEST',false,'none')); +$request['page']->drawTitle(sprintf('%s',_('Import'))); +$request['page']->drawSubTitle(sprintf('%s: %s %s: %s %s %s (%s)', + _('Server'),$app['server']->getName(), + _('File'),$request['import']->getSource('name'),number_format($request['import']->getSource('size')),_('bytes'),$type['description'])); + +echo '
'; + +# @todo When renaming DNs, the hotlink should point to the new entry on success, or the old entry on failure. +while (! $request['import']->eof()) { + while ($request['template'] = $request['import']->readEntry()) { + + $edit_href = sprintf('cmd.php?cmd=template_engine&server_id=%s&dn=%s',$app['server']->getIndex(), + rawurlencode($request['template']->getDN())); + + $changetype = $request['template']->getType(); + printf('%s
%s',$actionString[$changetype],$edit_href,$request['template']->getDN()); + + if ($request['import']->LDAPimport()) + printf(' %s
',_('Success')); + + else { + printf(' %s

',_('Failed')); + $errormsg = sprintf('%s %s',$actionErrorMsg[$changetype],$request['template']->getDN()); + $errormsg .= ldap_error_msg($app['server']->getErrorMessage(null),$app['server']->getErrorNum(null)); + + system_message(array( + 'title'=>_('LDIF text import'), + 'body'=>$errormsg, + 'type'=>'warn')); + } + } + + if ($request['import']->error) { + printf('%s: %s
', + _('Error'),$request['import']->error['message']); + + echo '
'; + + display_pla_parse_error($request['import']); + } + + if (! $request['continuous_mode']) + break; +} + +function display_pla_parse_error($request) { + $type = $request->getType(); + + echo '