Best Python code snippet using playwright-python
test_contentmanager.py
Source:test_contentmanager.py
...21 cm.add_get_handler(key, foo_getter)22 m = self._make_message()23 m['Content-Type'] = 'text/plain'24 m['X-Bar-Header'] = 'foo'25 self.assertEqual(cm.get_content(m, foo='bar'), ('bar', 'foo'))26 def get_key_as_get_content_key_order(self, order, key):27 def bar_getter(msg):28 return msg['X-Bar-Header']29 def foo_getter(msg):30 return msg['X-Foo-Header']31 cm = ContentManager()32 cm.add_get_handler(key, foo_getter)33 for precedence, key in self.get_key_params.values():34 if precedence > order:35 cm.add_get_handler(key, bar_getter)36 m = self._make_message()37 m['Content-Type'] = 'text/plain'38 m['X-Bar-Header'] = 'bar'39 m['X-Foo-Header'] = 'foo'40 self.assertEqual(cm.get_content(m), ('foo'))41 def test_get_content_raises_if_unknown_mimetype_and_no_default(self):42 cm = ContentManager()43 m = self._make_message()44 m['Content-Type'] = 'text/plain'45 with self.assertRaisesRegex(KeyError, 'text/plain'):46 cm.get_content(m)47 class BaseThing(str):48 pass49 baseobject_full_path = __name__ + '.' + 'TestContentManager.BaseThing'50 class Thing(BaseThing):51 pass52 testobject_full_path = __name__ + '.' + 'TestContentManager.Thing'53 set_key_params = {54 'type': (0, Thing,),55 'full_path': (1, testobject_full_path,),56 'qualname': (2, 'TestContentManager.Thing',),57 'name': (3, 'Thing',),58 'base_type': (4, BaseThing,),59 'base_full_path': (5, baseobject_full_path,),60 'base_qualname': (6, 'TestContentManager.BaseThing',),61 'base_name': (7, 'BaseThing',),62 'str_type': (8, str,),63 'str_full_path': (9, 'builtins.str',),64 'str_name': (10, 'str',), # str name and qualname are the same65 'null_key': (11, None,),66 }67 def set_key_as_set_content_key(self, order, key):68 def foo_setter(msg, obj, foo=None):69 msg['X-Foo-Header'] = foo70 msg.set_payload(obj)71 cm = ContentManager()72 cm.add_set_handler(key, foo_setter)73 m = self._make_message()74 msg_obj = self.Thing()75 cm.set_content(m, msg_obj, foo='bar')76 self.assertEqual(m['X-Foo-Header'], 'bar')77 self.assertEqual(m.get_payload(), msg_obj)78 def set_key_as_set_content_key_order(self, order, key):79 def foo_setter(msg, obj):80 msg['X-FooBar-Header'] = 'foo'81 msg.set_payload(obj)82 def bar_setter(msg, obj):83 msg['X-FooBar-Header'] = 'bar'84 cm = ContentManager()85 cm.add_set_handler(key, foo_setter)86 for precedence, key in self.get_key_params.values():87 if precedence > order:88 cm.add_set_handler(key, bar_setter)89 m = self._make_message()90 msg_obj = self.Thing()91 cm.set_content(m, msg_obj)92 self.assertEqual(m['X-FooBar-Header'], 'foo')93 self.assertEqual(m.get_payload(), msg_obj)94 def test_set_content_raises_if_unknown_type_and_no_default(self):95 cm = ContentManager()96 m = self._make_message()97 msg_obj = self.Thing()98 with self.assertRaisesRegex(KeyError, self.testobject_full_path):99 cm.set_content(m, msg_obj)100 def test_set_content_raises_if_called_on_multipart(self):101 cm = ContentManager()102 m = self._make_message()103 m['Content-Type'] = 'multipart/foo'104 with self.assertRaises(TypeError):105 cm.set_content(m, 'test')106 def test_set_content_calls_clear_content(self):107 m = self._make_message()108 m['Content-Foo'] = 'bar'109 m['Content-Type'] = 'text/html'110 m['To'] = 'test'111 m.set_payload('abc')112 cm = ContentManager()113 cm.add_set_handler(str, lambda *args, **kw: None)114 m.set_content('xyz', content_manager=cm)115 self.assertIsNone(m['Content-Foo'])116 self.assertIsNone(m['Content-Type'])117 self.assertEqual(m['To'], 'test')118 self.assertIsNone(m.get_payload())119@parameterize120class TestRawDataManager(TestEmailBase):121 # Note: these tests are dependent on the order in which headers are added122 # to the message objects by the code. There's no defined ordering in123 # RFC5322/MIME, so this makes the tests more fragile than the standards124 # require. However, if the header order changes it is best to understand125 # *why*, and make sure it isn't a subtle bug in whatever change was126 # applied.127 policy = policy.default.clone(max_line_length=60,128 content_manager=raw_data_manager)129 message = EmailMessage130 def test_get_text_plain(self):131 m = self._str_msg(textwrap.dedent("""\132 Content-Type: text/plain133 Basic text.134 """))135 self.assertEqual(raw_data_manager.get_content(m), "Basic text.\n")136 def test_get_text_html(self):137 m = self._str_msg(textwrap.dedent("""\138 Content-Type: text/html139 <p>Basic text.</p>140 """))141 self.assertEqual(raw_data_manager.get_content(m),142 "<p>Basic text.</p>\n")143 def test_get_text_plain_latin1(self):144 m = self._bytes_msg(textwrap.dedent("""\145 Content-Type: text/plain; charset=latin1146 Basìc tëxt.147 """).encode('latin1'))148 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")149 def test_get_text_plain_latin1_quoted_printable(self):150 m = self._str_msg(textwrap.dedent("""\151 Content-Type: text/plain; charset="latin-1"152 Content-Transfer-Encoding: quoted-printable153 Bas=ECc t=EBxt.154 """))155 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")156 def test_get_text_plain_utf8_base64(self):157 m = self._str_msg(textwrap.dedent("""\158 Content-Type: text/plain; charset="utf8"159 Content-Transfer-Encoding: base64160 QmFzw6xjIHTDq3h0Lgo=161 """))162 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt.\n")163 def test_get_text_plain_bad_utf8_quoted_printable(self):164 m = self._str_msg(textwrap.dedent("""\165 Content-Type: text/plain; charset="utf8"166 Content-Transfer-Encoding: quoted-printable167 Bas=c3=acc t=c3=abxt=fd.168 """))169 self.assertEqual(raw_data_manager.get_content(m), "Basìc tëxt�.\n")170 def test_get_text_plain_bad_utf8_quoted_printable_ignore_errors(self):171 m = self._str_msg(textwrap.dedent("""\172 Content-Type: text/plain; charset="utf8"173 Content-Transfer-Encoding: quoted-printable174 Bas=c3=acc t=c3=abxt=fd.175 """))176 self.assertEqual(raw_data_manager.get_content(m, errors='ignore'),177 "Basìc tëxt.\n")178 def test_get_text_plain_utf8_base64_recoverable_bad_CTE_data(self):179 m = self._str_msg(textwrap.dedent("""\180 Content-Type: text/plain; charset="utf8"181 Content-Transfer-Encoding: base64182 QmFzw6xjIHTDq3h0Lgo\xFF=183 """))184 self.assertEqual(raw_data_manager.get_content(m, errors='ignore'),185 "Basìc tëxt.\n")186 def test_get_text_invalid_keyword(self):187 m = self._str_msg(textwrap.dedent("""\188 Content-Type: text/plain189 Basic text.190 """))191 with self.assertRaises(TypeError):192 raw_data_manager.get_content(m, foo='ignore')193 def test_get_non_text(self):194 template = textwrap.dedent("""\195 Content-Type: {}196 Content-Transfer-Encoding: base64197 Ym9ndXMgZGF0YQ==198 """)199 for maintype in 'audio image video application'.split():200 with self.subTest(maintype=maintype):201 m = self._str_msg(template.format(maintype+'/foo'))202 self.assertEqual(raw_data_manager.get_content(m), b"bogus data")203 def test_get_non_text_invalid_keyword(self):204 m = self._str_msg(textwrap.dedent("""\205 Content-Type: image/jpg206 Content-Transfer-Encoding: base64207 Ym9ndXMgZGF0YQ==208 """))209 with self.assertRaises(TypeError):210 raw_data_manager.get_content(m, errors='ignore')211 def test_get_raises_on_multipart(self):212 m = self._str_msg(textwrap.dedent("""\213 Content-Type: multipart/mixed; boundary="==="214 --===215 --===--216 """))217 with self.assertRaises(KeyError):218 raw_data_manager.get_content(m)219 def test_get_message_rfc822_and_external_body(self):220 template = textwrap.dedent("""\221 Content-Type: message/{}222 To: foo@example.com223 From: bar@example.com224 Subject: example225 an example message226 """)227 for subtype in 'rfc822 external-body'.split():228 with self.subTest(subtype=subtype):229 m = self._str_msg(template.format(subtype))230 sub_msg = raw_data_manager.get_content(m)231 self.assertIsInstance(sub_msg, self.message)232 self.assertEqual(raw_data_manager.get_content(sub_msg),233 "an example message\n")234 self.assertEqual(sub_msg['to'], 'foo@example.com')235 self.assertEqual(sub_msg['from'].addresses[0].username, 'bar')236 def test_get_message_non_rfc822_or_external_body_yields_bytes(self):237 m = self._str_msg(textwrap.dedent("""\238 Content-Type: message/partial239 To: foo@example.com240 From: bar@example.com241 Subject: example242 The real body is in another message.243 """))244 self.assertEqual(raw_data_manager.get_content(m)[:10], b'To: foo@ex')245 def test_set_text_plain(self):246 m = self._make_message()247 content = "Simple message.\n"248 raw_data_manager.set_content(m, content)249 self.assertEqual(str(m), textwrap.dedent("""\250 Content-Type: text/plain; charset="utf-8"251 Content-Transfer-Encoding: 7bit252 Simple message.253 """))254 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)255 self.assertEqual(m.get_content(), content)256 def test_set_text_html(self):257 m = self._make_message()258 content = "<p>Simple message.</p>\n"259 raw_data_manager.set_content(m, content, subtype='html')260 self.assertEqual(str(m), textwrap.dedent("""\261 Content-Type: text/html; charset="utf-8"262 Content-Transfer-Encoding: 7bit263 <p>Simple message.</p>264 """))265 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)266 self.assertEqual(m.get_content(), content)267 def test_set_text_charset_latin_1(self):268 m = self._make_message()269 content = "Simple message.\n"270 raw_data_manager.set_content(m, content, charset='latin-1')271 self.assertEqual(str(m), textwrap.dedent("""\272 Content-Type: text/plain; charset="iso-8859-1"273 Content-Transfer-Encoding: 7bit274 Simple message.275 """))276 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)277 self.assertEqual(m.get_content(), content)278 def test_set_text_short_line_minimal_non_ascii_heuristics(self):279 m = self._make_message()280 content = "et là il est monté sur moi et il commence à m'éto.\n"281 raw_data_manager.set_content(m, content)282 self.assertEqual(bytes(m), textwrap.dedent("""\283 Content-Type: text/plain; charset="utf-8"284 Content-Transfer-Encoding: 8bit285 et là il est monté sur moi et il commence à m'éto.286 """).encode('utf-8'))287 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)288 self.assertEqual(m.get_content(), content)289 def test_set_text_long_line_minimal_non_ascii_heuristics(self):290 m = self._make_message()291 content = ("j'ai un problème de python. il est sorti de son"292 " vivarium. et là il est monté sur moi et il commence"293 " à m'éto.\n")294 raw_data_manager.set_content(m, content)295 self.assertEqual(bytes(m), textwrap.dedent("""\296 Content-Type: text/plain; charset="utf-8"297 Content-Transfer-Encoding: quoted-printable298 j'ai un probl=C3=A8me de python. il est sorti de son vivari=299 um. et l=C3=A0 il est mont=C3=A9 sur moi et il commence =300 =C3=A0 m'=C3=A9to.301 """).encode('utf-8'))302 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)303 self.assertEqual(m.get_content(), content)304 def test_set_text_11_lines_long_line_minimal_non_ascii_heuristics(self):305 m = self._make_message()306 content = '\n'*10 + (307 "j'ai un problème de python. il est sorti de son"308 " vivarium. et là il est monté sur moi et il commence"309 " à m'éto.\n")310 raw_data_manager.set_content(m, content)311 self.assertEqual(bytes(m), textwrap.dedent("""\312 Content-Type: text/plain; charset="utf-8"313 Content-Transfer-Encoding: quoted-printable314 """ + '\n'*10 + """315 j'ai un probl=C3=A8me de python. il est sorti de son vivari=316 um. et l=C3=A0 il est mont=C3=A9 sur moi et il commence =317 =C3=A0 m'=C3=A9to.318 """).encode('utf-8'))319 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)320 self.assertEqual(m.get_content(), content)321 def test_set_text_maximal_non_ascii_heuristics(self):322 m = self._make_message()323 content = "áà äéèÄöÅ.\n"324 raw_data_manager.set_content(m, content)325 self.assertEqual(bytes(m), textwrap.dedent("""\326 Content-Type: text/plain; charset="utf-8"327 Content-Transfer-Encoding: 8bit328 áà äéèÄöÅ.329 """).encode('utf-8'))330 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)331 self.assertEqual(m.get_content(), content)332 def test_set_text_11_lines_maximal_non_ascii_heuristics(self):333 m = self._make_message()334 content = '\n'*10 + "áà äéèÄöÅ.\n"335 raw_data_manager.set_content(m, content)336 self.assertEqual(bytes(m), textwrap.dedent("""\337 Content-Type: text/plain; charset="utf-8"338 Content-Transfer-Encoding: 8bit339 """ + '\n'*10 + """340 áà äéèÄöÅ.341 """).encode('utf-8'))342 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)343 self.assertEqual(m.get_content(), content)344 def test_set_text_long_line_maximal_non_ascii_heuristics(self):345 m = self._make_message()346 content = ("áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"347 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"348 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ.\n")349 raw_data_manager.set_content(m, content)350 self.assertEqual(bytes(m), textwrap.dedent("""\351 Content-Type: text/plain; charset="utf-8"352 Content-Transfer-Encoding: base64353 w6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TDqcOoxJnD354 tsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TDqcOo355 xJnDtsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOgw6TD356 qcOoxJnDtsWRw6HDoMOkw6nDqMSZw7bFkcOhw6DDpMOpw6jEmcO2xZHDocOg357 w6TDqcOoxJnDtsWRLgo=358 """).encode('utf-8'))359 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)360 self.assertEqual(m.get_content(), content)361 def test_set_text_11_lines_long_line_maximal_non_ascii_heuristics(self):362 # Yes, it chooses "wrong" here. It's a heuristic. So this result363 # could change if we come up with a better heuristic.364 m = self._make_message()365 content = ('\n'*10 +366 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"367 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"368 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ.\n")369 raw_data_manager.set_content(m, "\n"*10 +370 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"371 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ"372 "áà äéèÄöÅáà äéèÄöÅáà äéèÄöÅáà äéèÄöÅ.\n")373 self.assertEqual(bytes(m), textwrap.dedent("""\374 Content-Type: text/plain; charset="utf-8"375 Content-Transfer-Encoding: quoted-printable376 """ + '\n'*10 + """377 =C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=378 =A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=379 =C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=380 =A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=381 =C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=382 =91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=383 =C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=384 =A4=C3=A9=C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=385 =C3=A8=C4=99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=386 =99=C3=B6=C5=91=C3=A1=C3=A0=C3=A4=C3=A9=C3=A8=C4=99=C3=B6=387 =C5=91.388 """).encode('utf-8'))389 self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)390 self.assertEqual(m.get_content(), content)391 def test_set_text_non_ascii_with_cte_7bit_raises(self):392 m = self._make_message()393 with self.assertRaises(UnicodeError):394 raw_data_manager.set_content(m,"áà äéèÄöÅ.\n", cte='7bit')395 def test_set_text_non_ascii_with_charset_ascii_raises(self):396 m = self._make_message()397 with self.assertRaises(UnicodeError):398 raw_data_manager.set_content(m,"áà äéèÄöÅ.\n", charset='ascii')399 def test_set_text_non_ascii_with_cte_7bit_and_charset_ascii_raises(self):400 m = self._make_message()401 with self.assertRaises(UnicodeError):402 raw_data_manager.set_content(m,"áà äéèÄöÅ.\n", cte='7bit', charset='ascii')403 def test_set_message(self):404 m = self._make_message()405 m['Subject'] = "Forwarded message"406 content = self._make_message()407 content['To'] = 'python@vivarium.org'408 content['From'] = 'police@monty.org'409 content['Subject'] = "get back in your box"410 content.set_content("Or face the comfy chair.")411 raw_data_manager.set_content(m, content)412 self.assertEqual(str(m), textwrap.dedent("""\413 Subject: Forwarded message414 Content-Type: message/rfc822415 Content-Transfer-Encoding: 8bit416 To: python@vivarium.org417 From: police@monty.org418 Subject: get back in your box419 Content-Type: text/plain; charset="utf-8"420 Content-Transfer-Encoding: 7bit421 MIME-Version: 1.0422 Or face the comfy chair.423 """))424 payload = m.get_payload(0)425 self.assertIsInstance(payload, self.message)426 self.assertEqual(str(payload), str(content))427 self.assertIsInstance(m.get_content(), self.message)428 self.assertEqual(str(m.get_content()), str(content))429 def test_set_message_with_non_ascii_and_coercion_to_7bit(self):430 m = self._make_message()431 m['Subject'] = "Escape report"432 content = self._make_message()433 content['To'] = 'police@monty.org'434 content['From'] = 'victim@monty.org'435 content['Subject'] = "Help"436 content.set_content("j'ai un problème de python. il est sorti de son"437 " vivarium.")438 raw_data_manager.set_content(m, content)439 self.assertEqual(bytes(m), textwrap.dedent("""\440 Subject: Escape report441 Content-Type: message/rfc822442 Content-Transfer-Encoding: 8bit443 To: police@monty.org444 From: victim@monty.org445 Subject: Help446 Content-Type: text/plain; charset="utf-8"447 Content-Transfer-Encoding: 8bit448 MIME-Version: 1.0449 j'ai un problème de python. il est sorti de son vivarium.450 """).encode('utf-8'))451 # The choice of base64 for the body encoding is because generator452 # doesn't bother with heuristics and uses it unconditionally for utf-8453 # text.454 # XXX: the first cte should be 7bit, too...that's a generator bug.455 # XXX: the line length in the body also looks like a generator bug.456 self.assertEqual(m.as_string(maxheaderlen=self.policy.max_line_length),457 textwrap.dedent("""\458 Subject: Escape report459 Content-Type: message/rfc822460 Content-Transfer-Encoding: 8bit461 To: police@monty.org462 From: victim@monty.org463 Subject: Help464 Content-Type: text/plain; charset="utf-8"465 Content-Transfer-Encoding: base64466 MIME-Version: 1.0467 aidhaSB1biBwcm9ibMOobWUgZGUgcHl0aG9uLiBpbCBlc3Qgc29ydGkgZGUgc29uIHZpdmFyaXVt468 Lgo=469 """))470 self.assertIsInstance(m.get_content(), self.message)471 self.assertEqual(str(m.get_content()), str(content))472 def test_set_message_invalid_cte_raises(self):473 m = self._make_message()474 content = self._make_message()475 for cte in 'quoted-printable base64'.split():476 for subtype in 'rfc822 external-body'.split():477 with self.subTest(cte=cte, subtype=subtype):478 with self.assertRaises(ValueError) as ar:479 m.set_content(content, subtype, cte=cte)480 exc = str(ar.exception)481 self.assertIn(cte, exc)482 self.assertIn(subtype, exc)483 subtype = 'external-body'484 for cte in '8bit binary'.split():485 with self.subTest(cte=cte, subtype=subtype):486 with self.assertRaises(ValueError) as ar:487 m.set_content(content, subtype, cte=cte)488 exc = str(ar.exception)489 self.assertIn(cte, exc)490 self.assertIn(subtype, exc)491 def test_set_image_jpg(self):492 for content in (b"bogus content",493 bytearray(b"bogus content"),494 memoryview(b"bogus content")):495 with self.subTest(content=content):496 m = self._make_message()497 raw_data_manager.set_content(m, content, 'image', 'jpeg')498 self.assertEqual(str(m), textwrap.dedent("""\499 Content-Type: image/jpeg500 Content-Transfer-Encoding: base64501 Ym9ndXMgY29udGVudA==502 """))503 self.assertEqual(m.get_payload(decode=True), content)504 self.assertEqual(m.get_content(), content)505 def test_set_audio_aif_with_quoted_printable_cte(self):506 # Why you would use qp, I don't know, but it is technically supported.507 # XXX: the incorrect line length is because binascii.b2a_qp doesn't508 # support a line length parameter, but we must use it to get newline509 # encoding.510 # XXX: what about that lack of tailing newline? Do we actually handle511 # that correctly in all cases? That is, if the *source* has an512 # unencoded newline, do we add an extra newline to the returned payload513 # or not? And can that actually be disambiguated based on the RFC?514 m = self._make_message()515 content = b'b\xFFgus\tcon\nt\rent ' + b'z'*100516 m.set_content(content, 'audio', 'aif', cte='quoted-printable')517 self.assertEqual(bytes(m), textwrap.dedent("""\518 Content-Type: audio/aif519 Content-Transfer-Encoding: quoted-printable520 MIME-Version: 1.0521 b=FFgus=09con=0At=0Dent=20zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz=522 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz""").encode('latin-1'))523 self.assertEqual(m.get_payload(decode=True), content)524 self.assertEqual(m.get_content(), content)525 def test_set_video_mpeg_with_binary_cte(self):526 m = self._make_message()527 content = b'b\xFFgus\tcon\nt\rent ' + b'z'*100528 m.set_content(content, 'video', 'mpeg', cte='binary')529 self.assertEqual(bytes(m), textwrap.dedent("""\530 Content-Type: video/mpeg531 Content-Transfer-Encoding: binary532 MIME-Version: 1.0533 """).encode('ascii') +534 # XXX: the second \n ought to be a \r, but generator gets it wrong.535 # THIS MEANS WE DON'T ACTUALLY SUPPORT THE 'binary' CTE.536 b'b\xFFgus\tcon\nt\nent zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' +537 b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz')538 self.assertEqual(m.get_payload(decode=True), content)539 self.assertEqual(m.get_content(), content)540 def test_set_application_octet_stream_with_8bit_cte(self):541 # In 8bit mode, univeral line end logic applies. It is up to the542 # application to make sure the lines are short enough; we don't check.543 m = self._make_message()544 content = b'b\xFFgus\tcon\nt\rent\n' + b'z'*60 + b'\n'545 m.set_content(content, 'application', 'octet-stream', cte='8bit')546 self.assertEqual(bytes(m), textwrap.dedent("""\547 Content-Type: application/octet-stream548 Content-Transfer-Encoding: 8bit549 MIME-Version: 1.0550 """).encode('ascii') +551 b'b\xFFgus\tcon\nt\nent\n' +552 b'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\n')553 self.assertEqual(m.get_payload(decode=True), content)554 self.assertEqual(m.get_content(), content)555 def test_set_headers_from_header_objects(self):556 m = self._make_message()557 content = "Simple message.\n"558 header_factory = self.policy.header_factory559 raw_data_manager.set_content(m, content, headers=(560 header_factory("To", "foo@example.com"),561 header_factory("From", "foo@example.com"),562 header_factory("Subject", "I'm talking to myself.")))563 self.assertEqual(str(m), textwrap.dedent("""\564 Content-Type: text/plain; charset="utf-8"565 To: foo@example.com566 From: foo@example.com567 Subject: I'm talking to myself.568 Content-Transfer-Encoding: 7bit569 Simple message.570 """))571 def test_set_headers_from_strings(self):572 m = self._make_message()573 content = "Simple message.\n"574 raw_data_manager.set_content(m, content, headers=(575 "X-Foo-Header: foo",576 "X-Bar-Header: bar",))577 self.assertEqual(str(m), textwrap.dedent("""\578 Content-Type: text/plain; charset="utf-8"579 X-Foo-Header: foo580 X-Bar-Header: bar581 Content-Transfer-Encoding: 7bit582 Simple message.583 """))584 def test_set_headers_with_invalid_duplicate_string_header_raises(self):585 m = self._make_message()586 content = "Simple message.\n"587 with self.assertRaisesRegex(ValueError, 'Content-Type'):588 raw_data_manager.set_content(m, content, headers=(589 "Content-Type: foo/bar",)590 )591 def test_set_headers_with_invalid_duplicate_header_header_raises(self):592 m = self._make_message()593 content = "Simple message.\n"594 header_factory = self.policy.header_factory595 with self.assertRaisesRegex(ValueError, 'Content-Type'):596 raw_data_manager.set_content(m, content, headers=(597 header_factory("Content-Type", " foo/bar"),)598 )599 def test_set_headers_with_defective_string_header_raises(self):600 m = self._make_message()601 content = "Simple message.\n"602 with self.assertRaisesRegex(ValueError, 'a@fairly@@invalid@address'):603 raw_data_manager.set_content(m, content, headers=(604 'To: a@fairly@@invalid@address',)605 )606 print(m['To'].defects)607 def test_set_headers_with_defective_header_header_raises(self):608 m = self._make_message()609 content = "Simple message.\n"610 header_factory = self.policy.header_factory611 with self.assertRaisesRegex(ValueError, 'a@fairly@@invalid@address'):612 raw_data_manager.set_content(m, content, headers=(613 header_factory('To', 'a@fairly@@invalid@address'),)614 )615 print(m['To'].defects)616 def test_set_disposition_inline(self):617 m = self._make_message()618 m.set_content('foo', disposition='inline')619 self.assertEqual(m['Content-Disposition'], 'inline')620 def test_set_disposition_attachment(self):621 m = self._make_message()622 m.set_content('foo', disposition='attachment')623 self.assertEqual(m['Content-Disposition'], 'attachment')624 def test_set_disposition_foo(self):625 m = self._make_message()626 m.set_content('foo', disposition='foo')627 self.assertEqual(m['Content-Disposition'], 'foo')628 # XXX: we should have a 'strict' policy mode (beyond raise_on_defect) that629 # would cause 'foo' above to raise.630 def test_set_filename(self):631 m = self._make_message()632 m.set_content('foo', filename='bar.txt')633 self.assertEqual(m['Content-Disposition'],634 'attachment; filename="bar.txt"')635 def test_set_filename_and_disposition_inline(self):636 m = self._make_message()637 m.set_content('foo', disposition='inline', filename='bar.txt')638 self.assertEqual(m['Content-Disposition'], 'inline; filename="bar.txt"')639 def test_set_non_ascii_filename(self):640 m = self._make_message()641 m.set_content('foo', filename='ábárî.txt')642 self.assertEqual(bytes(m), textwrap.dedent("""\643 Content-Type: text/plain; charset="utf-8"644 Content-Transfer-Encoding: 7bit645 Content-Disposition: attachment;646 filename*=utf-8''%C3%A1b%C3%A1r%C3%AE.txt647 MIME-Version: 1.0648 foo649 """).encode('ascii'))650 content_object_params = {651 'text_plain': ('content', ()),652 'text_html': ('content', ('html',)),653 'application_octet_stream': (b'content',654 ('application', 'octet_stream')),655 'image_jpeg': (b'content', ('image', 'jpeg')),656 'message_rfc822': (message(), ()),657 'message_external_body': (message(), ('external-body',)),658 }659 def content_object_as_header_receiver(self, obj, mimetype):660 m = self._make_message()661 m.set_content(obj, *mimetype, headers=(662 'To: foo@example.com',663 'From: bar@simple.net'))664 self.assertEqual(m['to'], 'foo@example.com')665 self.assertEqual(m['from'], 'bar@simple.net')666 def content_object_as_disposition_inline_receiver(self, obj, mimetype):667 m = self._make_message()668 m.set_content(obj, *mimetype, disposition='inline')669 self.assertEqual(m['Content-Disposition'], 'inline')670 def content_object_as_non_ascii_filename_receiver(self, obj, mimetype):671 m = self._make_message()672 m.set_content(obj, *mimetype, disposition='inline', filename='bár.txt')673 self.assertEqual(m['Content-Disposition'], 'inline; filename="bár.txt"')674 self.assertEqual(m.get_filename(), "bár.txt")675 self.assertEqual(m['Content-Disposition'].params['filename'], "bár.txt")676 def content_object_as_cid_receiver(self, obj, mimetype):677 m = self._make_message()678 m.set_content(obj, *mimetype, cid='some_random_stuff')679 self.assertEqual(m['Content-ID'], 'some_random_stuff')680 def content_object_as_params_receiver(self, obj, mimetype):681 m = self._make_message()682 params = {'foo': 'bár', 'abc': 'xyz'}683 m.set_content(obj, *mimetype, params=params)684 if isinstance(obj, str):685 params['charset'] = 'utf-8'686 self.assertEqual(m['Content-Type'].params, params)687if __name__ == '__main__':...
default.py
Source:default.py
1#!/usr/bin/python2# -*- coding: utf-8 -*-3import xbmc, xbmcgui, xbmcaddon, urllib, urllib2, socket, cookielib, re, os, shutil, base64, xbmcvfs4addon = xbmcaddon.Addon()5socket.setdefaulttimeout(60)6addonID = addon.getAddonInfo('id')7translation = addon.getLocalizedString8addonUserDataFolder=xbmc.translatePath("special://profile/addon_data/"+addonID)9subTempDir=xbmc.translatePath("special://profile/addon_data/"+addonID+"/srtTemp/")10icon = xbmc.translatePath('special://home/addons/'+addonID+'/icon.png')11rarFile=xbmc.translatePath(addonUserDataFolder+"/sub.")12subFile=xbmc.translatePath(addonUserDataFolder+"/sub.srt")13favFile=xbmc.translatePath(addonUserDataFolder+"/favourites")14apiKeyFile=xbmc.translatePath(addonUserDataFolder+"/api.key")15if not os.path.isdir(addonUserDataFolder):16 os.mkdir(addonUserDataFolder)17if not os.path.isdir(subTempDir):18 os.mkdir(subTempDir)19if os.path.exists(apiKeyFile):20 fh = open(apiKeyFile, 'r')21 ownKey = fh.read()22 fh.close()23else:24 ownKey=""25user=""26pw=""27backNav=""28pause=""29saveSub=""30language=""31def getSettings():32 global user33 user=addon.getSetting("user")34 global pw35 pw=addon.getSetting("pw")36 global backNav37 backNav=addon.getSetting("backNav")38 global pause39 pause=addon.getSetting("pause")40 global saveSub41 saveSub=addon.getSetting("saveSub")42 global language43 language=addon.getSetting("language")44getSettings()45if pause=="true" and xbmc.Player().isPlayingVideo():46 xbmc.Player().pause()47playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)48if playlist.getposition()>=0:49 currentTitle = playlist[playlist.getposition()].getdescription()50 51currentFile = xbmc.Player().getPlayingFile()52cj = cookielib.CookieJar()53mainUrl = "http://www.subcentral.de"54opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))55userAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0"56opener.addheaders = [('User-Agent', userAgent)]57opener.open(mainUrl+"/index.php?form=UserLogin", data="loginUsername="+urllib.quote_plus(user)+"&loginPassword="+urllib.quote_plus(pw))58while (user=="" or pw==""):59 addon.openSettings()60 getSettings()61currentEpisode=""62currentSeason=""63currentEpisode=xbmc.getInfoLabel('VideoPlayer.Episode')64currentSeason=xbmc.getInfoLabel('VideoPlayer.Season')65dirName = ""66try:67 dirName=(currentFile.split(os.sep)[-2]).lower()68except:69 pass70fileName=os.path.basename(currentFile).lower()71if currentEpisode=="":72 matchDir=re.compile('\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(dirName)73 matchFile=re.compile('\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(fileName)74 if len(matchDir)>0:75 currentSeason=matchDir[0][0]76 currentEpisode=matchDir[0][1]77 elif len(matchFile)>0:78 currentSeason=matchFile[0][0]79 currentEpisode=matchFile[0][1]80if currentEpisode=="":81 match=re.compile('(.+?)- s(.+?)e(.+?) ', re.DOTALL).findall(xbmc.getInfoLabel('VideoPlayer.Title').lower())82 if len(match)>0:83 currentSeason=match[0][1]84 currentEpisode=match[0][2]85if len(currentEpisode)==1:86 currentEpisode="0"+currentEpisode87if len(currentSeason)==1:88 currentSeason="0"+currentSeason89currentRelease=""90if "-" in fileName:91 currentRelease=(fileName.split("-")[-1]).lower()92 if "." in currentRelease:93 currentRelease=currentRelease[:currentRelease.find(".")]94elif "-" in dirName:95 currentRelease=(dirName.split("-")[-1]).lower()96def main():97 global mainType98 mainType = ""99 dialog = xbmcgui.Dialog()100 nr=dialog.select("SubCentral.de", [translation(30013),translation(30001),translation(30002),translation(30011)])101 if nr==0:102 search()103 elif nr==1:104 mainType="fav"105 showFavourites()106 elif nr==2:107 mainType="all"108 showAllSeries()109 elif nr==3:110 addon.openSettings()111 getSettings()112 main()113 else:114 if pause=="true" and xbmc.Player().isPlayingVideo():115 xbmc.Player().pause()116def search():117 title=xbmc.getInfoLabel('VideoPlayer.TVShowTitle')118 season=currentSeason119 episode=currentEpisode120 release=currentRelease121 122 if title=="" or season=="":123 matchDir=re.compile('(.+?)\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(dirName)124 matchFile=re.compile('(.+?)\\.s(.+?)e(.+?)\\.', re.DOTALL).findall(fileName)125 matchTitle=re.compile('(.+?)- s(.+?)e(.+?) ', re.DOTALL).findall(xbmc.getInfoLabel('VideoPlayer.Title').lower())126 if len(matchDir)>0:127 title=matchDir[0][0]128 elif len(matchFile)>0:129 title=matchFile[0][0]130 elif len(matchTitle)>0:131 title=matchTitle[0][0].strip()132 133 title=title.replace("."," ")134 if "(" in title:135 title=title[:title.find("(")].strip()136 137 if title=="" or season=="" or episode=="":138 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30014)+'!,3000,'+icon+')')139 main()140 else:141 if season[0:1]=="0":142 season=season[1:]143 general=base64.b64decode("QUl6YVN5RGJtNzRTNlZia1VjWmNQeC1HTTFtU1B5N2ZYU0R2Vy1J")144 global ownKey145 if ownKey=="":146 ownKey=general147 searchString='intitle:"[subs] '+title+' - staffel '+season+'|0'+season+'"'148 fullUrl="https://www.googleapis.com/customsearch/v1?key="+ownKey+"&cx=016144106520845837387:lj4yjfpwyna&q="+urllib.quote_plus(searchString)+"&alt=json"149 xbmc.log("SCDE Addon Log - Search: "+title+"#"+season)150 content = getUrl(fullUrl)151 if '"items"' in content:152 content = content[content.find('"items"'):]153 spl=content.split('"kind"')154 finalUrl=""155 for i in range(1,len(spl),1):156 entry=spl[i]157 match=re.compile('"title": "(.+?)"', re.DOTALL).findall(entry)158 title=match[0]159 match=re.compile('"link": "(.+?)"', re.DOTALL).findall(entry)160 url=match[0]161 if "staffel" in title.lower() and "subs:" in title.lower() and "postID=" not in url and "boardID=" not in url:162 finalUrl=url163 break164 if finalUrl=="":165 for i in range(1,len(spl),1):166 entry=spl[i]167 match=re.compile('"title": "(.+?)"', re.DOTALL).findall(entry)168 title=match[0]169 match=re.compile('"link": "(.+?)"', re.DOTALL).findall(entry)170 url=match[0]171 if "staffel" in title.lower() and "postID=" not in url and "boardID=" not in url:172 finalUrl=url173 break174 175 if len(season)==1:176 season="0"+season177 178 if finalUrl!="":179 threadID = finalUrl[finalUrl.find("&threadID=")+10:]180 if "&" in threadID:181 threadID = threadID[:threadID.find("&")]182 content = opener.open("http://www.subcentral.de/index.php?page=Thread&threadID="+threadID).read()183 if 'class="bedankhinweisSC' in content:184 contentThanks=content185 contentThanks=contentThanks[contentThanks.find('class="bedankhinweisSC'):]186 match=re.compile('href="index.php\\?action=Thank(.+?)"', re.DOTALL).findall(contentThanks)187 matchID=re.compile('name="threadID" value="(.+?)"', re.DOTALL).findall(contentThanks)188 dialog = xbmcgui.Dialog()189 nr=dialog.select(title, [translation(30005)+"..."])190 if nr>=0:191 opener.open(mainUrl+"/index.php?action=Thank"+match[0].replace("&","&"))192 content = opener.open(mainUrl+"/index.php?page=Thread&threadID="+matchID[0]).read()193 194 attachments = []195 titles = []196 197 match=re.compile('<title>(.+?)-', re.DOTALL).findall(content)198 tvShowTitle=match[0].replace("[Subs]","").strip()199 200 content = content[content.find("quoteData.set('post-")+1:]201 content = content[:content.find("quoteData.set('post-")]202 contentDE=content203 contentEN=""204 if '<img src="creative/bilder/flags/usa.png"' in content:205 contentDE=content[:content.find('<img src="creative/bilder/flags/usa.png"')]206 contentEN=content[content.find('<img src="creative/bilder/flags/usa.png"'):]207 elif '<img src="creative/bilder/flags/uk.png"' in content:208 contentDE=content[:content.find('<img src="creative/bilder/flags/uk.png"')]209 contentEN=content[content.find('<img src="creative/bilder/flags/uk.png"'):]210 elif 'Englische Untertitel:' in content:211 contentDE=content[:content.find('Englische Untertitel:')]212 contentEN=content[content.find('Englische Untertitel:'):]213 if "page=Attachment&attachmentID=" not in contentDE:214 contentDE=content215 216 if language=="0":217 tempDE=appendSubInfo(tvShowTitle,season,episode,release,contentDE,"DE")218 attachments += tempDE[0]219 titles += tempDE[1]220 if contentEN!="":221 tempEN=appendSubInfo(tvShowTitle,season,episode,release,contentEN,"EN")222 attachments += tempEN[0]223 titles += tempEN[1]224 elif language=="1":225 tempDE=appendSubInfo(tvShowTitle,season,episode,release,contentDE,"DE")226 attachments += tempDE[0]227 titles += tempDE[1]228 elif language=="2" and contentEN!="":229 tempEN=appendSubInfo(tvShowTitle,season,episode,release,contentEN,"EN")230 attachments += tempEN[0]231 titles += tempEN[1]232 233 if len(titles)>0:234 titles, attachments = (list(x) for x in zip(*sorted(zip(titles, attachments))))235 dialog = xbmcgui.Dialog()236 nr=dialog.select(os.path.basename(currentFile), titles)237 if nr>=0:238 subUrl=mainUrl+"/index.php?page=Attachment&attachmentID="+attachments[nr]239 setSubtitle(subUrl)240 elif backNav=="true":241 main()242 else:243 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30015)+'!,3000,'+icon+')')244 main()245 elif '"totalResults": "0"' in content:246 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30015)+'!,3000,'+icon+')')247 main()248 else:249 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30016)+'!,10000,'+icon+')')250 main()251def getEpisodes(entry):252 ep=ep2=""253 match=re.compile('>E(.+?) -', re.DOTALL).findall(entry,0,10)254 match2=re.compile('>(.+?)x(.+?) -', re.DOTALL).findall(entry,0,10)255 match3=re.compile('>(.+?)\\. ', re.DOTALL).findall(entry,0,10)256 match4=re.compile('>(.+?) -', re.DOTALL).findall(entry,0,10)257 match5=re.compile('>(.+?)<', re.DOTALL).findall(entry,0,10)258 if "- komplett<" in entry.lower():259 ep=ep2=" - Komplett"260 else:261 if len(match)>0:262 ep=match[0]263 elif len(match2):264 ep=match2[0][1]265 elif len(match3):266 ep=match3[0]267 elif len(match4):268 ep=match4[0]269 elif len(match5):270 ep=match5[0]271 else:272 ep="00"273 ep2="E"+ep274 return [ep,ep2]275def appendSubInfo(tvShowTitle,season,episode,release,content,lang):276 attachments1 = []277 titles1 = []278 content2=content.replace('<span class=" Stil36 Stil31">','').replace('<span class="Stil37">','')279 match=re.compile('<div align="center"><a href="http://www.subcentral.de/index.php\\?page=Attachment&attachmentID=(.+?)">⪠E(.+?) \\((.+?)\\)</a>', re.DOTALL).findall(content2)280 for attach, ep, rel in match:281 ep2="E"+ep282 if episode==ep:283 check = release==""284 if release!="":285 check = release==rel.lower()286 if check:287 if attach not in attachments1:288 attachments1.append(attach)289 titles1.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))290 if len(attachments1)==0:291 match=re.compile('<div align="center"><a href="http://www.subcentral.de/index.php\\?page=Attachment&attachmentID=(.+?)">⪠E(.+?) \\((.+?)\\)</a>', re.DOTALL).findall(content2)292 for attach, ep, rel in match:293 ep2="E"+ep294 if episode==ep:295 if attach not in attachments1:296 attachments1.append(attach)297 titles1.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))298 if len(attachments1)==0:299 match=re.compile('<div align="center"><a href="http://www.subcentral.de/index.php\\?page=Attachment&attachmentID=(.+?)">⪠E(.+?) \\((.+?)\\)</a>', re.DOTALL).findall(content2)300 for attach, ep, rel in match:301 ep2="E"+ep302 if attach not in attachments1:303 attachments1.append(attach)304 titles1.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))305 attachments2 = []306 titles2 = []307 splitStr = ""308 if 'class="release"' in content:309 splitStr = 'class="release"'310 elif 'class="Stil9"' in content:311 splitStr = 'class="Stil9"'312 if splitStr:313 spl=content.split(splitStr)314 for i in range(1,len(spl),1):315 entry=spl[i].replace("<strong>","").replace("</strong>","").replace('<span style="font-size: 8pt">','')316 temp = getEpisodes(entry)317 ep = temp[0]318 ep2 = temp[1]319 match=re.compile('index.php\\?page=Attachment&attachmentID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)320 for attach, rel in match:321 if episode==ep:322 check = release==""323 if release!="":324 check = release==rel.lower()325 if check:326 if attach not in attachments2:327 attachments2.append(attach)328 titles2.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))329 if len(attachments2)==0:330 spl=content.split(splitStr)331 for i in range(1,len(spl),1):332 entry=spl[i].replace("<strong>","").replace("</strong>","").replace('<span style="font-size: 8pt">','')333 temp = getEpisodes(entry)334 ep = temp[0]335 ep2 = temp[1]336 match=re.compile('index.php\\?page=Attachment&attachmentID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)337 for attach, rel in match:338 if episode==ep:339 if attach not in attachments2:340 attachments2.append(attach)341 titles2.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))342 if len(attachments2)==0:343 spl=content.split(splitStr)344 for i in range(1,len(spl),1):345 entry=spl[i].replace("<strong>","").replace("</strong>","").replace('<span style="font-size: 8pt">','')346 temp = getEpisodes(entry)347 ep = temp[0]348 ep2 = temp[1]349 match=re.compile('index.php\\?page=Attachment&attachmentID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)350 for attach, rel in match:351 if attach not in attachments2:352 attachments2.append(attach)353 titles2.append(lang+" - "+tvShowTitle+" - S"+season+ep2+" - "+rel.replace("</span>",""))354 return [attachments1+attachments2,titles1+titles2]355def showFavourites():356 ids = []357 titles = []358 if os.path.exists(favFile):359 fh = open(favFile, 'r')360 for line in fh:361 id = line[:line.find("#")]362 title = line[line.find("#")+1:]363 title = title[:title.find("#END")]364 ids.append(id)365 titles.append(title)366 fh.close()367 titles, ids = (list(x) for x in zip(*sorted(zip(titles, ids))))368 dialog = xbmcgui.Dialog()369 nr=dialog.select(translation(30001), titles)370 if nr>=0:371 id=ids[nr]372 title=titles[nr]373 showSeries(id)374 elif backNav=="true":375 main()376def showAllSeries():377 content = opener.open(mainUrl+"/index.php").read()378 content = content[content.find('<option value=""> Serien QuickJump </option>')+1:]379 content = content[:content.find('</form>')]380 match=re.compile('<option value="(.+?)">(.+?)</option>', re.DOTALL).findall(content)381 threadIDs = []382 threadNames = []383 for id, title in match:384 threadIDs.append(id)385 threadNames.append(title)386 dialog = xbmcgui.Dialog()387 nr=dialog.select(translation(30002), threadNames)388 if nr>=0:389 id=threadIDs[nr]390 title=threadNames[nr]391 showSeries(id)392 elif backNav=="true":393 main()394def showSeries(seriesID):395 content = opener.open(mainUrl+"/index.php?page=Board&boardID="+seriesID).read()396 match=re.compile('<title>(.+?) -', re.DOTALL).findall(content)397 SeriesTitle=match[0]398 content = content[content.find("<h3>Wichtige Themen</h3>"):]399 content = content[:content.find('</table>')]400 spl=content.split('<p id="threadTitle')401 threadIDs = []402 threadNames = []403 season=currentSeason404 if season[0:1]=="0":405 season=season[1:]406 for i in range(1,len(spl),1):407 entry=spl[i]408 match=re.compile('<a href="index.php\\?page=Thread&threadID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)409 if ("staffel "+season in match[0][1].lower() or "staffel 0"+season in match[0][1].lower()) and "subs" in match[0][1].lower():410 threadIDs.append(match[0][0])411 threadNames.append(cleanTitle(match[0][1]))412 if len(threadIDs)==0:413 for i in range(1,len(spl),1):414 entry=spl[i]415 match=re.compile('<a href="index.php\\?page=Thread&threadID=(.+?)">(.+?)</a>', re.DOTALL).findall(entry)416 if "subs" in match[0][1].lower():417 threadIDs.append(match[0][0])418 threadNames.append(cleanTitle(match[0][1]))419 threadNames, threadIDs = (list(x) for x in zip(*sorted(zip(threadNames, threadIDs))))420 content=""421 if os.path.exists(favFile):422 fh = open(favFile, 'r')423 content=fh.read()424 fh.close()425 if seriesID+"#" not in content:426 threadNames.append(translation(30003))427 else:428 threadNames.append(translation(30004))429 dialog = xbmcgui.Dialog()430 nr=dialog.select(os.path.basename(currentFile), threadNames)431 if nr>=0:432 if nr==len(threadNames)-1:433 if threadNames[nr]==translation(30003):434 addToFavourites(seriesID,SeriesTitle)435 elif threadNames[nr]==translation(30004):436 removeFromFavourites(seriesID,SeriesTitle)437 showSeries(seriesID)438 else:439 id=threadIDs[nr]440 showSubtitles(seriesID,id)441 elif backNav=="true":442 if mainType=="all":443 showAllSeries()444 elif mainType=="fav":445 showFavourites()446def showSubtitles(seriesID,id):447 content = opener.open(mainUrl+"/index.php?page=Thread&threadID="+id).read()448 match=re.compile('<title>(.+?)</title>', re.DOTALL).findall(content)449 title=match[0]450 if 'class="bedankhinweisSC' in content:451 contentThanks=content452 contentThanks=contentThanks[contentThanks.find('class="bedankhinweisSC'):]453 match=re.compile('href="index.php\\?action=Thank(.+?)"', re.DOTALL).findall(contentThanks)454 matchID=re.compile('name="threadID" value="(.+?)"', re.DOTALL).findall(contentThanks)455 dialog = xbmcgui.Dialog()456 nr=dialog.select(title, [translation(30005)+"..."])457 if nr>=0:458 opener.open(mainUrl+"/index.php?action=Thank"+match[0].replace("&","&"))459 content = opener.open(mainUrl+"/index.php?page=Thread&threadID="+id).read()460 elif backNav=="true":461 showSeries(seriesID)462 attachments = []463 titles = []464 465 match=re.compile('<title>(.+?)-', re.DOTALL).findall(content)466 tvShowTitle=match[0].replace("[Subs]","").strip()467 match=re.compile('Staffel (.+?) ', re.DOTALL).findall(content)468 season=match[0]469 if len(season)==1:470 season="0"+season471 472 content = content[content.find("quoteData.set('post-")+1:]473 content = content[:content.find("quoteData.set('post-")]474 contentDE=content475 contentEN=""476 if '<img src="creative/bilder/flags/usa.png"' in content:477 contentDE=content[:content.find('<img src="creative/bilder/flags/usa.png"')]478 contentEN=content[content.find('<img src="creative/bilder/flags/usa.png"'):]479 elif '<img src="creative/bilder/flags/uk.png"' in content:480 contentDE=content[:content.find('<img src="creative/bilder/flags/uk.png"')]481 contentEN=content[content.find('<img src="creative/bilder/flags/uk.png"'):]482 elif 'Englische Untertitel:' in content:483 contentDE=content[:content.find('Englische Untertitel:')]484 contentEN=content[content.find('Englische Untertitel:'):]485 if "page=Attachment&attachmentID=" not in contentDE:486 contentDE=content487 488 if language=="0":489 tempDE=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentDE,"DE")490 attachments += tempDE[0]491 titles += tempDE[1]492 if contentEN!="":493 tempEN=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentEN,"EN")494 attachments += tempEN[0]495 titles += tempEN[1]496 elif language=="1":497 tempDE=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentDE,"DE")498 attachments += tempDE[0]499 titles += tempDE[1]500 elif language=="2" and contentEN!="":501 tempEN=appendSubInfo(tvShowTitle,season,currentEpisode,currentRelease,contentEN,"EN")502 attachments += tempEN[0]503 titles += tempEN[1]504 505 if len(titles)>0:506 titles, attachments = (list(x) for x in zip(*sorted(zip(titles, attachments))))507 dialog = xbmcgui.Dialog()508 nr=dialog.select(os.path.basename(currentFile), titles)509 if nr>=0:510 subUrl=mainUrl+"/index.php?page=Attachment&attachmentID="+attachments[nr]511 setSubtitle(subUrl)512 elif backNav=="true":513 showSeries(seriesID)514def setSubtitle(subUrl):515 clearSubTempDir()516 rarContent = opener.open(subUrl).read()517 if rarContent.startswith("Rar"):518 ext="rar"519 else:520 ext="zip"521 global rarFile522 rarFile=rarFile+ext523 fh = open(rarFile, 'wb')524 fh.write(rarContent)525 fh.close()526 xbmc.executebuiltin("XBMC.Extract("+rarFile+", "+subTempDir+")")527 xbmc.sleep(1000)528 files = os.listdir(subTempDir)529 tempFile=""530 if len(files)>1:531 dialog = xbmcgui.Dialog()532 nr=dialog.select(currentTitle, files)533 if nr>=0:534 tempFile = xbmc.translatePath(subTempDir+"/"+files[nr])535 else:536 clearSubTempDir()537 if backNav=="true":538 main()539 elif len(files)!=0:540 tempFile = xbmc.translatePath(subTempDir+"/"+files[0])541 else:542 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30017)+'!,3000,'+icon+')')543 if pause=="true" and xbmc.Player().isPlayingVideo():544 xbmc.Player().pause()545 if tempFile!="":546 shutil.copy2(tempFile, subFile)547 if saveSub=="true" and "http://" not in currentFile and "plugin://" not in currentFile:548 try:549 extLength = len(currentFile.split(".")[-1])550 archiveFile = currentFile[:-extLength]+"srt"551 xbmcvfs.copy(tempFile, archiveFile)552 global subFile553 subFile = archiveFile554 except:555 pass556 clearSubTempDir()557 xbmc.Player().setSubtitles(subFile)558 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+translation(30012)+'!,2000,'+icon+')')559 if pause=="true" and xbmc.Player().isPlayingVideo():560 xbmc.Player().pause()561def clearSubTempDir():562 files = os.listdir(subTempDir)563 for file in files:564 try:565 os.remove(xbmc.translatePath(subTempDir+"/"+file))566 except:567 pass568def addToFavourites(seriesID,title):569 entry=seriesID+"#"+title+"#END"570 if os.path.exists(favFile):571 fh = open(favFile, 'r')572 content=fh.read()573 fh.close()574 if entry not in content:575 fh=open(favFile, 'a')576 fh.write(entry+"\n")577 fh.close()578 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+title+': '+translation(30008)+',3000,'+icon+')')579 else:580 fh=open(favFile, 'a')581 fh.write(entry+"\n")582 fh.close()583def removeFromFavourites(seriesID,title):584 newContent=""585 fh = open(favFile, 'r')586 for line in fh:587 if seriesID+"#" not in line:588 newContent+=line589 fh.close()590 fh=open(favFile, 'w')591 fh.write(newContent)592 fh.close()593 xbmc.executebuiltin('XBMC.Notification(SubCentral.de:,'+title+': '+translation(30009)+',3000,'+icon+')')594def getUrl(url):595 req = urllib2.Request(url)596 req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; rv:23.0) Gecko/20100101 Firefox/23.0')597 response = urllib2.urlopen(req)598 content=response.read()599 response.close()600 return content601def cleanTitle(title):602 title=title.replace("<","<").replace(">",">").replace("&","&").replace("'","'").replace(""","\"").replace("ß","Ã").replace("–","-")603 title=title.replace("Ä","Ã").replace("Ü","Ã").replace("Ö","Ã").replace("ä","ä").replace("ü","ü").replace("ö","ö")604 title=title.strip()605 return title...
test_message.py
Source:test_message.py
...367 allparts = list(m.walk())368 parts = [allparts[n] for n in parts]369 self.assertEqual(list(m.iter_parts()), parts)370 class _TestContentManager:371 def get_content(self, msg, *args, **kw):372 return msg, args, kw373 def set_content(self, msg, *args, **kw):374 self.msg = msg375 self.args = args376 self.kw = kw377 def test_get_content_with_cm(self):378 m = self._str_msg('')379 cm = self._TestContentManager()380 self.assertEqual(m.get_content(content_manager=cm), (m, (), {}))381 msg, args, kw = m.get_content('foo', content_manager=cm, bar=1, k=2)382 self.assertEqual(msg, m)383 self.assertEqual(args, ('foo',))384 self.assertEqual(kw, dict(bar=1, k=2))385 def test_get_content_default_cm_comes_from_policy(self):386 p = policy.default.clone(content_manager=self._TestContentManager())387 m = self._str_msg('', policy=p)388 self.assertEqual(m.get_content(), (m, (), {}))389 msg, args, kw = m.get_content('foo', bar=1, k=2)390 self.assertEqual(msg, m)391 self.assertEqual(args, ('foo',))392 self.assertEqual(kw, dict(bar=1, k=2))393 def test_set_content_with_cm(self):394 m = self._str_msg('')395 cm = self._TestContentManager()396 m.set_content(content_manager=cm)397 self.assertEqual(cm.msg, m)398 self.assertEqual(cm.args, ())399 self.assertEqual(cm.kw, {})400 m.set_content('foo', content_manager=cm, bar=1, k=2)401 self.assertEqual(cm.msg, m)402 self.assertEqual(cm.args, ('foo',))403 self.assertEqual(cm.kw, dict(bar=1, k=2))404 def test_set_content_default_cm_comes_from_policy(self):405 cm = self._TestContentManager()406 p = policy.default.clone(content_manager=cm)407 m = self._str_msg('', policy=p)408 m.set_content()409 self.assertEqual(cm.msg, m)410 self.assertEqual(cm.args, ())411 self.assertEqual(cm.kw, {})412 m.set_content('foo', bar=1, k=2)413 self.assertEqual(cm.msg, m)414 self.assertEqual(cm.args, ('foo',))415 self.assertEqual(cm.kw, dict(bar=1, k=2))416 # outcome is whether xxx_method should raise ValueError error when called417 # on multipart/subtype. Blank outcome means it depends on xxx (add418 # succeeds, make raises). Note: 'none' means there are content-type419 # headers but payload is None...this happening in practice would be very420 # unusual, so treating it as if there were content seems reasonable.421 # method subtype outcome422 subtype_params = (423 ('related', 'no_content', 'succeeds'),424 ('related', 'none', 'succeeds'),425 ('related', 'plain', 'succeeds'),426 ('related', 'related', ''),427 ('related', 'alternative', 'raises'),428 ('related', 'mixed', 'raises'),429 ('alternative', 'no_content', 'succeeds'),430 ('alternative', 'none', 'succeeds'),431 ('alternative', 'plain', 'succeeds'),432 ('alternative', 'related', 'succeeds'),433 ('alternative', 'alternative', ''),434 ('alternative', 'mixed', 'raises'),435 ('mixed', 'no_content', 'succeeds'),436 ('mixed', 'none', 'succeeds'),437 ('mixed', 'plain', 'succeeds'),438 ('mixed', 'related', 'succeeds'),439 ('mixed', 'alternative', 'succeeds'),440 ('mixed', 'mixed', ''),441 )442 def _make_subtype_test_message(self, subtype):443 m = self.message()444 payload = None445 msg_headers = [446 ('To', 'foo@bar.com'),447 ('From', 'bar@foo.com'),448 ]449 if subtype != 'no_content':450 ('content-shadow', 'Logrus'),451 msg_headers.append(('X-Random-Header', 'Corwin'))452 if subtype == 'text':453 payload = ''454 msg_headers.append(('Content-Type', 'text/plain'))455 m.set_payload('')456 elif subtype != 'no_content':457 payload = []458 msg_headers.append(('Content-Type', 'multipart/' + subtype))459 msg_headers.append(('X-Trump', 'Random'))460 m.set_payload(payload)461 for name, value in msg_headers:462 m[name] = value463 return m, msg_headers, payload464 def _check_disallowed_subtype_raises(self, m, method_name, subtype, method):465 with self.assertRaises(ValueError) as ar:466 getattr(m, method)()467 exc_text = str(ar.exception)468 self.assertIn(subtype, exc_text)469 self.assertIn(method_name, exc_text)470 def _check_make_multipart(self, m, msg_headers, payload):471 count = 0472 for name, value in msg_headers:473 if not name.lower().startswith('content-'):474 self.assertEqual(m[name], value)475 count += 1476 self.assertEqual(len(m), count+1) # +1 for new Content-Type477 part = next(m.iter_parts())478 count = 0479 for name, value in msg_headers:480 if name.lower().startswith('content-'):481 self.assertEqual(part[name], value)482 count += 1483 self.assertEqual(len(part), count)484 self.assertEqual(part.get_payload(), payload)485 def subtype_as_make(self, method, subtype, outcome):486 m, msg_headers, payload = self._make_subtype_test_message(subtype)487 make_method = 'make_' + method488 if outcome in ('', 'raises'):489 self._check_disallowed_subtype_raises(m, method, subtype, make_method)490 return491 getattr(m, make_method)()492 self.assertEqual(m.get_content_maintype(), 'multipart')493 self.assertEqual(m.get_content_subtype(), method)494 if subtype == 'no_content':495 self.assertEqual(len(m.get_payload()), 0)496 self.assertEqual(m.items(),497 msg_headers + [('Content-Type',498 'multipart/'+method)])499 else:500 self.assertEqual(len(m.get_payload()), 1)501 self._check_make_multipart(m, msg_headers, payload)502 def subtype_as_make_with_boundary(self, method, subtype, outcome):503 # Doing all variation is a bit of overkill...504 m = self.message()505 if outcome in ('', 'raises'):506 m['Content-Type'] = 'multipart/' + subtype507 with self.assertRaises(ValueError) as cm:508 getattr(m, 'make_' + method)()509 return510 if subtype == 'plain':511 m['Content-Type'] = 'text/plain'512 elif subtype != 'no_content':513 m['Content-Type'] = 'multipart/' + subtype514 getattr(m, 'make_' + method)(boundary="abc")515 self.assertTrue(m.is_multipart())516 self.assertEqual(m.get_boundary(), 'abc')517 def test_policy_on_part_made_by_make_comes_from_message(self):518 for method in ('make_related', 'make_alternative', 'make_mixed'):519 m = self.message(policy=self.policy.clone(content_manager='foo'))520 m['Content-Type'] = 'text/plain'521 getattr(m, method)()522 self.assertEqual(m.get_payload(0).policy.content_manager, 'foo')523 class _TestSetContentManager:524 def set_content(self, msg, content, *args, **kw):525 msg['Content-Type'] = 'text/plain'526 msg.set_payload(content)527 def subtype_as_add(self, method, subtype, outcome):528 m, msg_headers, payload = self._make_subtype_test_message(subtype)529 cm = self._TestSetContentManager()530 add_method = 'add_attachment' if method=='mixed' else 'add_' + method531 if outcome == 'raises':532 self._check_disallowed_subtype_raises(m, method, subtype, add_method)533 return534 getattr(m, add_method)('test', content_manager=cm)535 self.assertEqual(m.get_content_maintype(), 'multipart')536 self.assertEqual(m.get_content_subtype(), method)537 if method == subtype or subtype == 'no_content':538 self.assertEqual(len(m.get_payload()), 1)539 for name, value in msg_headers:540 self.assertEqual(m[name], value)541 part = m.get_payload()[0]542 else:543 self.assertEqual(len(m.get_payload()), 2)544 self._check_make_multipart(m, msg_headers, payload)545 part = m.get_payload()[1]546 self.assertEqual(part.get_content_type(), 'text/plain')547 self.assertEqual(part.get_payload(), 'test')548 if method=='mixed':549 self.assertEqual(part['Content-Disposition'], 'attachment')550 elif method=='related':551 self.assertEqual(part['Content-Disposition'], 'inline')552 else:553 # Otherwise we don't guess.554 self.assertIsNone(part['Content-Disposition'])555 class _TestSetRaisingContentManager:556 def set_content(self, msg, content, *args, **kw):557 raise Exception('test')558 def test_default_content_manager_for_add_comes_from_policy(self):559 cm = self._TestSetRaisingContentManager()560 m = self.message(policy=self.policy.clone(content_manager=cm))561 for method in ('add_related', 'add_alternative', 'add_attachment'):562 with self.assertRaises(Exception) as ar:563 getattr(m, method)('')564 self.assertEqual(str(ar.exception), 'test')565 def message_as_clear(self, body_parts, attachments, parts, msg):566 m = self._str_msg(msg)567 m.clear()568 self.assertEqual(len(m), 0)569 self.assertEqual(list(m.items()), [])570 self.assertIsNone(m.get_payload())571 self.assertEqual(list(m.iter_parts()), [])572 def message_as_clear_content(self, body_parts, attachments, parts, msg):573 m = self._str_msg(msg)574 expected_headers = [h for h in m.keys()575 if not h.lower().startswith('content-')]576 m.clear_content()577 self.assertEqual(list(m.keys()), expected_headers)578 self.assertIsNone(m.get_payload())579 self.assertEqual(list(m.iter_parts()), [])580 def test_is_attachment(self):581 m = self._make_message()582 self.assertFalse(m.is_attachment)583 m['Content-Disposition'] = 'inline'584 self.assertFalse(m.is_attachment)585 m.replace_header('Content-Disposition', 'attachment')586 self.assertTrue(m.is_attachment)587 m.replace_header('Content-Disposition', 'AtTachMent')588 self.assertTrue(m.is_attachment)589class TestEmailMessage(TestEmailMessageBase, TestEmailBase):590 message = EmailMessage591 def test_set_content_adds_MIME_Version(self):592 m = self._str_msg('')593 cm = self._TestContentManager()594 self.assertNotIn('MIME-Version', m)595 m.set_content(content_manager=cm)596 self.assertEqual(m['MIME-Version'], '1.0')597 class _MIME_Version_adding_CM:598 def set_content(self, msg, *args, **kw):599 msg['MIME-Version'] = '1.0'600 def test_set_content_does_not_duplicate_MIME_Version(self):601 m = self._str_msg('')602 cm = self._MIME_Version_adding_CM()603 self.assertNotIn('MIME-Version', m)604 m.set_content(content_manager=cm)605 self.assertEqual(m['MIME-Version'], '1.0')606class TestMIMEPart(TestEmailMessageBase, TestEmailBase):607 # Doing the full test run here may seem a bit redundant, since the two608 # classes are almost identical. But what if they drift apart? So we do609 # the full tests so that any future drift doesn't introduce bugs.610 message = MIMEPart611 def test_set_content_does_not_add_MIME_Version(self):612 m = self._str_msg('')613 cm = self._TestContentManager()614 self.assertNotIn('MIME-Version', m)615 m.set_content(content_manager=cm)616 self.assertNotIn('MIME-Version', m)617if __name__ == '__main__':...
basecontenteditor.py
Source:basecontenteditor.py
1from zoundry.appframework.global_services import getLoggerService2from zoundry.appframework.ui.events.editcontrolevents import IZEditControlEvents3from zoundry.appframework.ui.events.toolbarevents import ZEVT_TOOLBAR_RESIZE4from zoundry.appframework.ui.util.colorutil import getDefaultDialogBackgroundColor5from zoundry.appframework.ui.widgets.controls.advanced.editcontrol import IZRichTextEditControl6from zoundry.appframework.ui.widgets.controls.common.acceleratortable import ZAcceleratorEntry7from zoundry.appframework.ui.widgets.controls.common.acceleratortable import ZAcceleratorTable8from zoundry.appframework.ui.widgets.controls.common.menu.menu import ZMenu9from zoundry.appframework.ui.widgets.controls.common.menu.menumodel import ZModelBasedMenuContentProvider10from zoundry.appframework.ui.widgets.controls.common.menu.menumodel import ZModelBasedMenuEventHandler11from zoundry.appframework.ui.widgets.controls.common.toolbar.toolbar import ZToolBar12from zoundry.appframework.ui.widgets.controls.common.toolbar.toolbarmodel import ZModelBasedToolBarContentProvider13from zoundry.appframework.ui.widgets.controls.common.toolbar.toolbarmodel import ZModelBasedToolBarEventHandler14from zoundry.appframework.ui.widgets.dialogs.standarddialogs import ZShowNotYetImplementedMessage15from zoundry.base.exceptions import ZAbstractMethodCalledException16from zoundry.blogapp.constants import IZBlogAppAcceleratorIds17from zoundry.blogapp.services.datastore.documentimpl import ZXhtmlContent18from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostEditorToolBarActionContext19from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostInsertImageAction20from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostInsertImgTagAction21from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostInsertLinkAction22from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogPostSpellCheckAction23from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZBlogRichTextFormatAction24from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZFocusOnTagwordsAction25from zoundry.blogapp.ui.actions.blogeditor.blogeditoractions import ZFocusOnTitleAction26from zoundry.blogapp.ui.editors.blogeditorctrls.blogposteditcontrol import IZBlogPostEditControl27from zoundry.blogapp.ui.editors.blogeditorctrls.metadata import ZBlogPostMetaDataWidget28from zoundry.blogapp.ui.menus.blogeditor.blogcontenteditorcontextmenumodel import ZBlogContentEditorContextMenuModel29from zoundry.blogapp.ui.menus.blogeditor.blogeditortoolbarmodel import ZBlogContentEditorToolbarModel30import wx3132# ------------------------------------------------------------------------------33# Interface that blog content editor controls must implement.34# ------------------------------------------------------------------------------35class IZBlogContentEditorControl:3637 def refreshUI(self):38 u"""refreshUI() -> void39 Updates the UI as well editor content based on the model data40 """ #$NON-NLS-1$41 # end refreshUI()4243 def updateModel(self):44 u"""updateModel() -> void45 Updates the model data (title, xhtml document etc) from the UI controls.46 """ #$NON-NLS-1$47 # end updateModel()4849 def modelSaved(self):50 u"""modelSaved() -> void51 Invoked by the editor framework when the model data has been persisted.52 """ #$NON-NLS-1$53 # end modelSaved()5455# end IZBlogContentEditorControl565758# ------------------------------------------------------------------------------59# Implements the accelerator table for the blog post content editor.60# ------------------------------------------------------------------------------61class ZBlogPostContentEditorAcceleratorTable(ZAcceleratorTable):6263 def __init__(self, context):64 self.context = context65 ZAcceleratorTable.__init__(self, IZBlogAppAcceleratorIds.ZID_BLOG_POST_EDITOR_CONTENT_ACCEL)66 # end __init__()6768 def _createActionContext(self):69 return self.context70 # end _createActionContext()7172 def _loadAdditionalEntries(self):73 return [74 # Bold, Italic, Underline, Strike75 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'B'), ZBlogRichTextFormatAction(IZRichTextEditControl.ZCAPABILITY_BOLD)), #$NON-NLS-1$76 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'I'), ZBlogRichTextFormatAction(IZRichTextEditControl.ZCAPABILITY_ITALIC)), #$NON-NLS-1$77 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'U'), ZBlogRichTextFormatAction(IZRichTextEditControl.ZCAPABILITY_UNDERLINE)), #$NON-NLS-1$78 # create link79 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'L'), ZBlogPostInsertLinkAction()), #$NON-NLS-1$80 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'K'), ZBlogPostInsertLinkAction()), #$NON-NLS-1$81 # insert image82 ZAcceleratorEntry(wx.ACCEL_CTRL, ord(u'M'), ZBlogPostInsertImageAction()), #$NON-NLS-1$,83 ZAcceleratorEntry(wx.ACCEL_CTRL + wx.ACCEL_SHIFT, ord(u'M'), ZBlogPostInsertImgTagAction()), #$NON-NLS-1$,84 # spell check85 ZAcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F7, ZBlogPostSpellCheckAction()),86 # Go to Title87 ZAcceleratorEntry(wx.ACCEL_ALT, ord(u'T'), ZFocusOnTitleAction()), #$NON-NLS-1$88 # Go to Tagwords89 ZAcceleratorEntry(wx.ACCEL_SHIFT, wx.WXK_TAB, ZFocusOnTagwordsAction()), #$NON-NLS-1$90 ]91 # end _loadAdditionalEntries()9293# end ZBlogPostContentEditorAcceleratorTable949596# ------------------------------------------------------------------------------97# This is the base class of the blog post content area editor.98# Concrete implementations are WsyiWyg and XhtmlText content editors99# ------------------------------------------------------------------------------100class ZBlogPostContentEditorBase(wx.Panel, IZBlogContentEditorControl):101102 def __init__(self, parentWindow, zblogPostEditor, zblogPostEditorModel):103 self.zblogPostEditor = zblogPostEditor104 self.zblogPostEditorModel = zblogPostEditorModel105 self.editor = None106 self.metaDataWidget = None107 self.contentEditCtrl = None108 # dirty: internally keep track if the editor (mshtml/scintilla) content has been modified.109 self.contentModified = False110 wx.Panel.__init__(self, parentWindow, wx.ID_ANY)111 self._createWidgets()112 self._layoutWidgets()113 self._bindWidgetEvents()114 if self._getContentEditControl():115 self._bindContentEditCtrlEvents()116 # end __init__()117118 def focusOnContent(self):119 self.contentEditCtrl.SetFocus()120 # end focusOnContent()121122 def getMetaDataWidget(self):123 return self.metaDataWidget124 # end getMetaDataWidget()125126 def _getModel(self):127 return self.zblogPostEditorModel128 # end _getModel129130 def _getContentEditControl(self):131 return self.contentEditCtrl132 # end _getContentEditControl133134 def _createWidgets(self):135 self.SetBackgroundColour(wx.Colour(255, 255, 255))136 self.metaDataWidget = ZBlogPostMetaDataWidget(self, self._getModel().getMetaDataModel() )137 self.metaDataWidget.SetBackgroundColour(getDefaultDialogBackgroundColor())138 self.contentEditCtrl = self._createContentEditCtrl(self)139 # Note: toolbar must be created only after the contentEditCtrl has been created (so that edit control capabilities can be determined)140 self.toolBar = self._createToolBar()141 self.tbStaticLine = wx.StaticLine(self, wx.ID_ANY)142143 self.acceleratorTable = ZBlogPostContentEditorAcceleratorTable(ZBlogPostEditorToolBarActionContext(self))144 self.contentEditCtrl.SetAcceleratorTable(self.acceleratorTable)145 # end _createWidgets()146147 def _createContentEditCtrl(self, parent):148 # sublcasses must create concrete IZBlogPostEditControl impl.149 raise ZAbstractMethodCalledException(self.__class__.__name__, u"_createContentEditCtrl") #$NON-NLS-1$150 # end _createContentEditCtrl()151152 def _layoutWidgets(self):153 self.sizer = wx.BoxSizer(wx.VERTICAL)154 self.sizer.Add(self.metaDataWidget, 0, wx.EXPAND)155 self.sizer.Add(self.toolBar, 0, wx.EXPAND)156 self.sizer.Add(self.tbStaticLine, 0, wx.EXPAND)157 self.sizer.Add(self.contentEditCtrl, 1, wx.EXPAND)158 self.SetSizer(self.sizer)159 self.SetAutoLayout(True)160 self.Layout()161 # end _layoutWidgets()162163 def _bindWidgetEvents(self):164 self.Bind(ZEVT_TOOLBAR_RESIZE, self.onToolBarResize, self.toolBar)165 wx.EVT_NAVIGATION_KEY(self.metaDataWidget, self.onMetaDataKeyboardNavigation)166 self.acceleratorTable.bindTo(self)167 # end _bindWidgetEvents()168169 def _bindContentEditCtrlEvents(self):170 self.Bind(IZEditControlEvents.ZEVT_UPDATE_UI, self.onUpdateUI, self._getContentEditControl())171 self.Bind(IZEditControlEvents.ZEVT_SELECTION_CHANGE, self.onSelectionChange, self._getContentEditControl())172 self.Bind(IZEditControlEvents.ZEVT_CONTEXT_MENU, self.onContextMenu, self._getContentEditControl())173 self.Bind(IZEditControlEvents.ZEVT_CONTENT_MODIFIED, self.onContentModified, self._getContentEditControl())174 # end _bindContentEditCtrlEvents()175176 def _createToolBar(self):177 self.toolBarModel = self._createToolBarModel()178 self.toolBarContext = ZBlogPostEditorToolBarActionContext(self)179 contentProvider = ZModelBasedToolBarContentProvider(self.toolBarModel, self.toolBarContext)180 eventHandler = ZModelBasedToolBarEventHandler(self.toolBarModel, self.toolBarContext)181 return ZToolBar(contentProvider, eventHandler, self)182 # end _createToolBar()183184 def _createToolBarModel(self):185 toolbarModel = ZBlogContentEditorToolbarModel()186 return toolbarModel187 # end _createToolBarModel()188189 def _isContentModified(self):190 return self.contentModified191 #end _isContentModified()192193 def _setContentModified(self, modified):194 self.contentModified = modified195196 def onUpdateUI(self, event): #@UnusedVariable197 self.zblogPostEditor._fireUpdateMenu() 198 self.toolBar.refresh()199 self._updateCaretPostionUI()200 # end onUIUpdate()201 202 def _updateCaretPostionUI(self):203 (row, col) = self._getContentEditControl().getCaretPosition()204 text = u"" #$NON-NLS-1$205 if row != -1 and col != -1:206 text = u"%d : %d" % (row, col) #$NON-NLS-1$207 self.zblogPostEditor.statusBarModel.setPaneText(u"rowcol", text) #$NON-NLS-1$208 self.zblogPostEditor._fireStatusBarChangedEvent()209 # end _updateCaretPostionUI()210211 def onContentModified(self, event): #@UnusedVariable212 # This is the event handler for mshtml/stc content modified/dirty indicator.213 # Notify the blog post editor just one time214 if not self._isContentModified():215 self.zblogPostEditor.setDirty(True)216 self._setContentModified(True)217 # end onContentModified()218219 def onSelectionChange(self, event): #@UnusedVariable220 pass221 # end onSelectionChange()222223 def onContextMenu(self, event):224 linkCtx = self.getLinkContext()225 imageCtx = self.getImageContext()226 tableCtx = self.getTableContext()227 removeExtMarker = False228 if self.hasCapability(IZBlogPostEditControl.ZCAPABILITY_EXTENDED_ENTRY_MARKER) \229 and self._getContentEditControl().canRemoveExtendedEntryMarker():230 removeExtMarker = True231232 menuModel = ZBlogContentEditorContextMenuModel()233 menuModel.initialize(linkCtx, imageCtx, tableCtx, removeExtMarker)234235 menuContext = self.zblogPostEditor.getMenuActionContext()236 contentProvider = ZModelBasedMenuContentProvider(menuModel, menuContext)237 eventHandler = ZModelBasedMenuEventHandler(menuModel, menuContext)238 menu = ZMenu(event.getParentWindow(), menuModel.getRootNode(), contentProvider, eventHandler)239 try:240 event.getParentWindow().PopupMenu(menu, event.getXYPoint())241 except Exception, e:242 getLoggerService().exception(e)243 menu.Destroy()244 # end onContextMenu()245246 def refreshUI(self):247 document = self.zblogPostEditorModel.getDocument()248 # refresh title, tags, blog data etc.249 self.metaDataWidget.refreshUI()250 # get xhtml content and set it in the edit control.251 if document.getContent() is not None:252 xhtmlDoc = document.getContent().getXhtmlDocument()253 self._getContentEditControl().setXhtmlDocument(xhtmlDoc)254 # end refreshUI()255256 def modelSaved(self):257 # model was saved to data store.258 # Clear dirty flag so that onContentModified() handler can set the flag in the model259 self._setContentModified(False)260 # Clear flags (such as 'content modified') in the content editor (eg. ZMSHTMLBlogPostEditControl via ZBlogPostWysiwygContentEditor)261 self._getContentEditControl().clearState()262 # Refresh widget UI.263 self.metaDataWidget.refreshUI()264 # end modelSaved()265266 def updateModel(self):267 document = self.zblogPostEditorModel.getDocument()268 # Flush title, tags, blog meta data etc. from UI controls to zmetadata model.269 self.metaDataWidget.updateModel()270 # Next copy title, tags etc. from meta data model to the document.271 self.zblogPostEditorModel.getMetaDataModel().updateDocument(document)272273 # Finally, get the Xhtml content from the editor and set it in the document274 xhtmlDoc = self._getContentEditControl().getXhtmlDocument()275 xhtmlContent = ZXhtmlContent()276 xhtmlContent.setXhtmlDocument(xhtmlDoc)277 document.setContent(xhtmlContent)278 # end updateModel()279280 def onTool(self, ctx): #@UnusedVariable281 ZShowNotYetImplementedMessage(self)282 # end onTool()283284 def onToolBarResize(self, event):285 self.Layout()286 event.Skip()287 # end onToolBarResize()288289 # This is a bit hack-y - when tabbing from the tagwords text control, in290 # the forward direction, the focus does not get properly set to the291 # content editor. I'm not really sure where it goes, actually. This292 # method will listen for keyboard nav events in the meta data widget and293 # re-route a forward direction Tab event so that it sets the focus294 # explicitly on the content editor.295 def onMetaDataKeyboardNavigation(self, event):296 focusWidget = self.metaDataWidget.FindFocus()297 if event.GetDirection() and event.IsFromTab() and focusWidget == self.metaDataWidget.tagwordsText:298 self.focusOnContent()299 else:300 event.Skip()301 # end onMetaDataKeyboardNavigation()302303 def hasCapability(self, capabilityId):304 return self._getContentEditControl().getCapabilities().hasCapability(capabilityId)305 # end hasCapability()306307 def canCut(self):308 return self._getContentEditControl().canCut()309 # end canCut()310311 def cut(self):312 self._getContentEditControl().cut()313 # end cut()314315 def canCopy(self):316 return self._getContentEditControl().canCopy()317 # end canCopy()318319 def copy(self):320 self._getContentEditControl().copy()321 # end copy()322323 def canPaste(self, xhtmlFormat):324 if xhtmlFormat:325 return self._getContentEditControl().canPasteXhtml()326 else:327 return self._getContentEditControl().canPaste()328 # end canPaste()329330 def paste(self, xhtmlFormat):331 if xhtmlFormat:332 self._getContentEditControl().pasteXhtml()333 else:334 self._getContentEditControl().paste()335 # end paste()336337 def canInsertXhtml(self):338 return self._getContentEditControl().canInsertXhtml()339 # end canInsertXhtml340341 def insertXhtml(self, xhtmlString): #@UnusedVariable342 self._getContentEditControl().insertXhtml(xhtmlString)343 # end insertXhtml344345 def canSelectAll(self):346 return self._getContentEditControl().canSelectAll()347 # end canSelectAll()348349 def selectAll(self):350 self._getContentEditControl().selectAll()351 # end selectAll()352353 def canUndo(self):354 return self._getContentEditControl().canUndo()355 # end canUndo()356357 def undo(self):358 self._getContentEditControl().undo()359 # end undo()360361 def canRedo(self):362 return self._getContentEditControl().canRedo()363 # end canRedo()364365 def redo(self):366 self._getContentEditControl().redo()367 # end redo()368369 # ---------------------------------------------370 # RichTextEdit formatting commands. Eg. Bold, Italic.371 # ---------------------------------------------372373 def isFormattingEnabled(self, capabilityId):374 return self._getContentEditControl().isFormattingEnabled(capabilityId)375 # end isFormattingEnabled()376377 def getFormattingState(self, capabilityId):378 return self._getContentEditControl().getFormattingState(capabilityId)379 # end getFormattingState()380381 def applyFormatting(self, capabilityId, customData):382 self._getContentEditControl().applyFormatting(capabilityId, customData)383 # end applyFormatting()384385 def getLinkContext(self):386 u"""getLinkContext() -> IZXHTMLEditControlLinkContext387 Returns link creation and edit context if available or None otherwise.""" #$NON-NLS-1$388 return self._getContentEditControl().getLinkContext()389 # end getLinkContext390391 def getImageContext(self):392 u"""getImageContext() -> IZXHTMLEditControlImageContext393 Returns image creation and edit context if available or None otherwise.""" #$NON-NLS-1$394 return self._getContentEditControl().getImageContext()395 # end getImageContext396397 def getTableContext(self):398 u"""getTableContext() -> IZXHTMLEditControlTableContext399 Returns table insertion and edit context if available or None otherwise.""" #$NON-NLS-1$400 return self._getContentEditControl().getTableContext()401 # end getTableContext402403 def getCurrentSelection(self):404 u"""getCurrentSelection() -> IZXHTMLEditControlSelection405 """ #$NON-NLS-1$406 return self._getContentEditControl().getCurrentSelection()407 # end getCurrentSelection()408409 # ---------------------------------------------410 # Extended entry411 # ---------------------------------------------412413 def insertExtendedEntryMarker(self):414 self._getContentEditControl().insertExtendedEntryMarker()415 # end insertExtendedEntryMarker()416417 def removeExtendedEntryMarker(self):418 self._getContentEditControl().removeExtendedEntryMarker()419 # removeExtendedEntryMarker()420421 # ---------------------------------------------422 # Spellcheck423 # ---------------------------------------------424 def createSpellCheckContext(self):425 u"""createSpellCheckContext() -> IZEditControlSpellCheckContext426 Returns IZEditControlSpellCheckContext if spellcheck is supported or None otherwise.427 """ #$NON-NLS-1$428 return self._getContentEditControl().createSpellCheckContext()429 # end createSpellCheckContext()430431 # ---------------------------------------------432 # Find and Find/Replace433 # ---------------------------------------------434 def createFindReplaceContext(self):435 u"""createFindReplaceContext() -> IZEditControlFindReplaceTextContext436 Returns IZEditControlFindReplaceTextContext if find/replace is supported or None otherwise.437 """ #$NON-NLS-1$438 return self._getContentEditControl().createFindReplaceContext()439 # end createFindReplaceContext()440 441 # ---------------------------------------------442 # Validate and Tidy443 # ---------------------------------------------444 def schemaValidate(self):445 self._getContentEditControl().schemaValidate()446 # end schemaValidate447 448 def clearValidation(self):449 self._getContentEditControl().clearValidation()450 # end clearValidation 451 452 def runTidy(self):453 self._getContentEditControl().runTidy()454 # end runTidy 455 456
...
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!